diff --git a/.github/workflows/ContinuousIntegration.yml b/.github/workflows/ContinuousIntegration.yml index 86bd741ee..f57079935 100644 --- a/.github/workflows/ContinuousIntegration.yml +++ b/.github/workflows/ContinuousIntegration.yml @@ -21,6 +21,12 @@ jobs: build_type: [ Debug, Release ] steps: + - name: Install hdf5 libraries for Linux + if: matrix.os == 'ubuntu-18.04' + run: sudo apt-get install libhdf5-dev + - name: Install hdf5 libraries for MacOS + if: matrix.os == 'macos-10.15' + run: brew install hdf5 - name: which CXX run: | which ${{matrix.cxx}} diff --git a/CMakeLists.txt b/CMakeLists.txt index a33c52da1..860548540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ target_link_libraries( GNDStk INTERFACE catch-adapter INTERFACE pugixml-adapter INTERFACE nlohmann_json::nlohmann_json + INTERFACE HighFive ) add_executable( json2class.exe diff --git a/autogen/.gitignore b/autogen/.gitignore index 321656c44..5de84783e 100644 --- a/autogen/.gitignore +++ b/autogen/.gitignore @@ -1 +1,2 @@ json2class.exe +test diff --git a/autogen/json2class.cpp b/autogen/json2class.cpp index ff4a91686..14fcb396a 100644 --- a/autogen/json2class.cpp +++ b/autogen/json2class.cpp @@ -4,8 +4,8 @@ // ----------------------------------------------------------------------------- #include "GNDStk.hpp" -#include "cstring" -using namespace njoy::GNDStk::core; +#include +using namespace njoy::GNDStk; // Report cases of nodes that have no metadata, and zero or one child node(s). // Where these exist, a simplification of the spec may be worth considering. @@ -62,7 +62,7 @@ struct InfoMetadata { // - a GNDStk::defaulted // but (in contrast with child nodes) can't make it be a vector of metadata. // An individual metadatum may be a vector in its own right, as in an XML - // construct such as (so that meta is a vector of + // construct such as (so that meta is a vector of // integers). We mean here that there isn't a vector of such [meta] entries, // and shouldn't be (XML wouldn't allow it). std::string name; @@ -157,10 +157,11 @@ struct PerClass { // as well as various processed information struct InfoSpecs { // From the .json file on the command line + std::string Path; + std::string Project; + std::string Version; std::string JSONDir; std::vector JSONFiles; - std::string GNDSDir; - std::string Version; // Version, but with '_' in place of '.' std::string VersionUnderscore; @@ -206,9 +207,7 @@ struct InfoSpecs { // Print text describing an action the code is about to take void action(const std::string &str) { - std::cout - << "\n" - << colors::plain::blue << str << "..." << colors::reset << std::endl; + std::cout << colors::plain::blue << str << colors::reset << std::endl; } // Is the string all whitespace? @@ -478,6 +477,11 @@ void getClassMetadata( }; if (m.defaultValue != "") { // If it has a default, then presumably it isn't required... + // fixme Should print a real, useful error message here. The mistake + // in question is something a user could easily make!! Look at other + // assert()s in this file as well; assert should be more for internal + // sanity checks than for diagnostic messages, as they aren't very + // helpful to typical users. assert(!metaRHS["required"]); } @@ -853,7 +857,8 @@ void writeClassPrefix(writer &out, const PerClass &per) void writeClassForComponent(writer &out, const PerClass &per) { // using [name for variant] = ... - out(); + if (per.variants.size()) + out(); for (const auto &v : per.variants) { out(1,"using @ = std::variant<", v.type); int count = 0, total = v.children.size(); @@ -926,7 +931,7 @@ void writeClassForComponent(writer &out, const PerClass &per) // Class suffix void writeClassSuffix( - writer &out, const PerClass &per, const std::string &version + writer &out, const PerClass &per, const InfoSpecs &specs ) { // assignment out(); @@ -946,8 +951,8 @@ void writeClassSuffix( out(1,"// Custom functionality"); out(1,smallComment); out(); - out(1,"#include \"GNDStk/@/@/@/src/custom.hpp\"", - version, per.nsname, per.clname); + out(1,"#include \"@/@/@/@/src/custom.hpp\"", + specs.Project, specs.Version, per.nsname, per.clname); // class+namespace end out(); @@ -958,7 +963,6 @@ void writeClassSuffix( - // ----------------------------------------------------------------------------- // writeClass* // For metadata, children, and variant children @@ -1098,24 +1102,31 @@ void writeClassSetterChild( out(1,"@ &@(const @ &obj)", parent.clname, child.name, child.typeFull); out(2,"{ @() = obj; return *this; }", child.name); - const auto indlab = - [&out,&parent,&child](const auto &T, const auto &par) - { - // T par: index or label parameter - out(); - out(1,"// @(@,value)", child.name, par); - out(1,"@ &@(", parent.clname, child.name); - out(2,"const @@,", T, par); - out(2,"const @ &obj", child.type); - out(1,") {"); - out(2,"@(@) = obj; return *this;", child.name, par); - out(1,"}"); - }; - - // with index or label + // if vector or optional if (child.isVector) { + const auto indlab = + [&out,&parent,&child](const auto &T, const auto &par) + { + // T par: index or label parameter + out(); + out(1,"// @(@,value)", child.name, par); + out(1,"@ &@(", parent.clname, child.name); + out(2,"const @@,", T, par); + out(2,"const @ &obj", child.type); + out(1,") {"); + out(2,"@(@) = obj; return *this;", child.name, par); + out(1,"}"); + }; + + // with index or label indlab("std::size_t ", "index"); indlab("std::string &", "label"); + + // push vector element + out(); + out(1,"// @(value) for vector push_back", child.name); + out(1,"@ &@(const @ &obj)", parent.clname, child.name, child.type); + out(2,"{ setter(@(), obj); return *this; }", child.name); } } // writeClassSetterChild @@ -1140,7 +1151,7 @@ void writeClassSetters(writer &out, const PerClass &per) out(1,"// @(value)", m.name); // special cases: we want to send length, start, and valueType - // to the BodyText base as well + // to the BlockData base as well const bool special = per.isData && (m.name == "length" || m.name == "start" || m.name == "valueType"); @@ -1148,26 +1159,17 @@ void writeClassSetters(writer &out, const PerClass &per) // setter // note that if type is optional, a T can still be sent out(1,"@ &@(const @ &obj)", per.clname, m.name, m.typeFull); - if (special && m.isDefaulted) - out(2,"{ BodyText::@(content.@ = obj); return *this; }", - m.name, m.name); - if (special && !m.isDefaulted) - out(2,"{ BodyText::@(@() = obj); return *this; }", - m.name, m.name); - if (!special && m.isDefaulted) - out(2,"{ content.@ = obj; return *this; }", - m.name); - if (!special && !m.isDefaulted) - out(2,"{ @() = obj; return *this; }", - m.name); + + special + ? out(2,"{ BlockData::@(@() = obj); return *this; }", m.name, m.name) + : out(2,"{ @() = obj; return *this; }", m.name); // setter, if type is Defaulted if (m.isDefaulted) { out(1,"@ &@(const std::optional<@> &obj)", per.clname, m.name, m.type); special - ? out(2,"{ BodyText::@(content.@ = obj); return *this; }", - m.name, m.name) - : out(2,"{ content.@ = obj; return *this; }", m.name); + ? out(2,"{ BlockData::@(@() = obj); return *this; }", m.name, m.name) + : out(2,"{ @() = obj; return *this; }", m.name); } } @@ -1220,22 +1222,22 @@ void writeClassSetters(writer &out, const PerClass &per) // writeClassCtorComponent void writeClassCtorComponent( - writer &out, const PerClass &per, const bool hasOther + writer &out, const PerClass &per, const bool copyOrMove ) { out(2,"Component{"); - out(3, hasOther ? "other" : "BodyText{}", false); + out(3, copyOrMove ? "other.baseBlockData()" : "BlockData{}", false); for (const auto &m : per.metadata) { // metadata out(","); - out(3,"content.@", m.name, false); + out(3,"this->@()", m.name, false); } for (const auto &c : per.children) { // children out(","); - out(3,"content.@", c.name, false); + out(3,"this->@()", c.name, false); } for (const auto &v : per.variants) { // variants out(","); - out(3,"content.@", v.name, false); + out(3,"this->@()", v.name, false); } out(); @@ -1255,48 +1257,23 @@ void writeClassCtorBody(writer &out, const std::string &argName) // writeClassCtors void writeClassCtors(writer &out, const PerClass &per) { - // ctor: default - out(); - out(1,"// default"); - out(1,"@() :", per.clname); - writeClassCtorComponent(out, per, false); - out(); - writeClassCtorBody(out, ""); - - // ctor: copy - out(); - out(1,"// copy"); - out(1,"@(const @ &other) :", per.clname, per.clname); - writeClassCtorComponent(out, per, true); - out(","); - out(2,"content{other.content}"); - writeClassCtorBody(out, "other"); - - // ctor: move - out(); - out(1,"// move"); - out(1,"@(@ &&other) :", per.clname, per.clname); - writeClassCtorComponent(out, per, true); - out(","); - out(2,"content{std::move(other.content)}"); - writeClassCtorBody(out, "other"); - - // ctor: node - out(); - out(1,"// from node"); - out(1,"@(const Node &node) :", per.clname); - writeClassCtorComponent(out, per, false); - out(); - writeClassCtorBody(out, "node"); - // ------------------------ - // ctor: fields + // ctor: default, + // and from fields // ------------------------ const auto total = per.nfields(); - if (total != 0) { + out(); + + if (total == 0) { + out(1,"// default"); + out(1,"@() :", per.clname); + writeClassCtorComponent(out, per, false); out(); - out(1,"// from fields"); + } else { + out(1,"// default, and from fields"); + + // informational message, if applicable for (const auto &m : per.metadata) if (m.isDefaulted) { out(1,"// std::optional replaces Defaulted; " @@ -1305,21 +1282,23 @@ void writeClassCtors(writer &out, const PerClass &per) } // signature, and base constructor call - // Note: we don't need "explicit" unless this constructor can be called - // with one argument. We'll always write it, however, in case someone - // modifies the auto-generated constructor (say, giving its arguments - // defaults) in such a way that is *can* be called with one argument. - // But we'd rather nobody modify the auto-generated classes. int count = 0; out(1,"explicit @(", per.clname); - for (const auto &m : per.metadata) - out(2,"const @ &@@", - m.isDefaulted ? "std::optional<" + m.type + ">" : m.typeFull, - m.name, ++count < total ? "," : ""); - for (const auto &c : per.children) - out(2,"const @ &@@", c.typeFull, c.name, ++count < total ? "," : ""); - for (const auto &v : per.variants) - out(2,"const @ &@@", v.typeFull, v.name, ++count < total ? "," : ""); + + for (const auto &m : per.metadata) { + const std::string type = + m.isDefaulted ? "std::optional<" + m.type + ">" : m.typeFull; + out(2,"const @ &@ =", type, m.name); + out(3,"@{}@", type, ++count < total ? "," : ""); + } + for (const auto &c : per.children) { + out(2,"const @ &@ =", c.typeFull, c.name); + out(3,"@{}@", c.typeFull, ++count < total ? "," : ""); + } + for (const auto &v : per.variants) { + out(2,"const @ &@ =", v.typeFull, v.name); + out(3,"@{}@", v.typeFull, ++count < total ? "," : ""); + } out(1,") :"); writeClassCtorComponent(out, per, false); @@ -1339,11 +1318,46 @@ void writeClassCtors(writer &out, const PerClass &per) for (const auto &v : per.variants) out(3,"@@", v.name, ++count < total ? "," : ""); out(2,"}"); - - // body - writeClassCtorBody(out, ""); } + // body + writeClassCtorBody(out, ""); + + // ------------------------ + // ctor: copy + // ------------------------ + + out(); + out(1,"// copy"); + out(1,"@(const @ &other) :", per.clname, per.clname); + writeClassCtorComponent(out, per, true); + out(","); + out(2,"content{other.content}"); + writeClassCtorBody(out, "other"); + + // ------------------------ + // ctor: move + // ------------------------ + + out(); + out(1,"// move"); + out(1,"@(@ &&other) :", per.clname, per.clname); + writeClassCtorComponent(out, per, true); + out(","); + out(2,"content{std::move(other.content)}"); + writeClassCtorBody(out, "other"); + + // ------------------------ + // ctor: node + // ------------------------ + + out(); + out(1,"// from node"); + out(1,"@(const Node &node) :", per.clname); + writeClassCtorComponent(out, per, false); + out(); + writeClassCtorBody(out, "node"); + // ------------------------ // ctor: vector // ------------------------ @@ -1352,7 +1366,7 @@ void writeClassCtors(writer &out, const PerClass &per) out(); out(1,"// from vector"); out(1,"template>>"); + "std::enable_if_t>>"); out(1,"@(const std::vector &vector) :", per.clname); writeClassCtorComponent(out, per, false); out(); @@ -1373,29 +1387,36 @@ void writeClass(PerClass &per, const InfoSpecs &specs) writer out(false); // output: class begin - writeClassPrefix(out,per); + writeClassPrefix(out, per); // output: for the Component base - writeClassForComponent(out,per); + writeClassForComponent(out, per); // output: using directives out(); out(1,"using Component::construct;"); if (per.isData) - out(1,"using BodyText::operator=;"); + out(1,"using BlockData::operator=;"); // output: defaults (applicable only to metadata) - out(); - out(1,smallComment); - out(1,"// Relevant defaults"); - out(1,"// FYI for users"); - out(1,smallComment); - out(); - out(1,"static inline const struct Defaults {"); + std::size_t ndefaults = 0; for (auto &m : per.metadata) if (m.isDefaulted) - out(2,"static inline const @ @ = @;", m.type, m.name, initializer(m)); - out(1,"} defaults;"); + ++ndefaults; + if (ndefaults > 0) { + out(); + out(1,smallComment); + out(1,"// Relevant defaults"); + out(1,"// FYI for users"); + out(1,smallComment); + out(); + out(1,"static inline const struct Defaults {"); + for (auto &m : per.metadata) + if (m.isDefaulted) + out(2, "static inline const @ @ = @;", + m.type, m.name, initializer(m)); + out(1,"} defaults;"); + } // output: content (the metadata/children computed earlier) out(); @@ -1418,12 +1439,12 @@ void writeClass(PerClass &per, const InfoSpecs &specs) // output: constructors out(); out(1,smallComment); - out(1,"// Construction"); + out(1,"// Constructors"); out(1,smallComment); writeClassCtors(out, per); // output: class end - writeClassSuffix(out, per, specs.Version); + writeClassSuffix(out, per, specs); // done per.code = out.str(); @@ -1550,9 +1571,11 @@ void printSingletons(const std::string &file) const auto metadata = getMetadataJSON(rhs); const auto children = getChildrenJSON(rhs); + const bool data = rhs.contains("data") && !rhs["data"].is_null(); + const bool body = rhs.contains("bodyText") && !rhs["bodyText"].is_null(); - if (metadata.size() == 0 && children.size() == 0) - log::info("Class \"{}\" has no metadata and no children", parent); + if (metadata.size() == 0 && children.size() == 0 && !data && !body) + log::info("Class \"{}\" has no metadata, children, or data", parent); if (metadata.size() == 0 && children.size() == 1) log::info("Class \"{}\" has no metadata and just one child", parent); } @@ -1565,11 +1588,12 @@ void commandLine( const int argc, const char *const *const argv, InfoSpecs &specs ) { - // Keys we'll look for + // JSON keys we'll look for + static const std::string path = "Path"; + static const std::string project = "Project"; + static const std::string version = "Version"; static const std::string input = "JSONDir"; static const std::string files = "JSONFiles"; - static const std::string output = "GNDSDir"; - static const std::string version = "Version"; static const std::string changes = "Changes"; // Usage @@ -1582,18 +1606,23 @@ void commandLine( const nlohmann::json jmain = readJSONFile(argv[1]); // Validate content - if (!(jmain.contains(input) && jmain.contains(output) && - jmain.contains(files) && jmain.contains(version))) { - log::error("The input json file needs {}, {}, {}, and {}", - input, files, output, version); + if (!(jmain.contains(version) && + jmain.contains(input) && + jmain.contains(files))) { + log::error("The input json file needs {}, {}, and {}", + version, input, files); throw std::exception{}; } // Extract information from the command line .json - specs.JSONDir = jmain[input]; - specs.JSONFiles = std::vector(jmain[files]); - specs.GNDSDir = jmain[output]; - specs.Version = jmain[version]; + specs.Path = jmain.contains(path) ? jmain[path] : "."; + specs.Project = jmain.contains(project) ? jmain[project] : "GNDStk"; + specs.Version = jmain[version]; + specs.JSONDir = jmain[input]; + for (const auto &str : jmain[files]) + specs.JSONFiles.push_back(str); + + // Version, with underscores in place of periods specs.VersionUnderscore = replace(specs.Version, '.', '_'); // Prepend the JSON file names with their directory @@ -1601,14 +1630,17 @@ void commandLine( file = specs.JSONDir + '/' + file; // File names - specs.hppVersion = specs.GNDSDir + "/src/GNDStk/" + specs.Version + ".hpp"; - specs.hppKey = specs.GNDSDir + "/src/GNDStk/" + specs.Version + "/key.hpp"; + const std::string base = + specs.Path + "/" + specs.Project + "/src/" + specs.Project + "/"; + specs.hppVersion = base + specs.Version + ".hpp"; + specs.hppKey = base + specs.Version + "/key.hpp"; // Report on "singletons" if (singletons) { - action("Finding possible simplifications"); + action("\nFinding possible simplifications..."); for (const std::string &file : specs.JSONFiles) printSingletons(file); + action("Done looking for simplifications."); } // Changes? @@ -1641,17 +1673,17 @@ void preprocessClass( // custom files as needed // ------------------------ - // Given the base GNDS directory and the GNDS version, as obtained earlier - // from the JSON input file to this tool, compute relevant directory names. - const std::string - // For the present namespace: C++ and Python directories. The present - // namespace probably contains multiple classes, so its directories - // may have been created already, but that's fine. - nsdir = specs.GNDSDir + "/src/GNDStk/" + specs.Version + "/" + nsname, - nsdirpy = specs.GNDSDir + "/python/src/" + specs.Version + "/" + nsname, - // For the present class: C++ source and test directories. - clsrc = nsdir + "/" + clname + "/src", - cltest = nsdir + "/" + clname + "/test"; + // For the present namespace: C++ and Python directories. The present + // namespace probably contains multiple classes, so these directories + // may have been created already, but that's fine. + const std::string nsdir = specs.Path + "/" + specs.Project + + "/src/" + specs.Project + "/" + specs.Version + "/" + nsname; + const std::string nsdirpy = specs.Path + "/" + specs.Project + + "/python/src" + "/" + specs.Version + "/" + nsname; + + // For the present class: C++ source and test directories. + const std::string clsrc = nsdir + "/" + clname + "/src"; + const std::string cltest = nsdir + "/" + clname + "/test"; // Create the above directories, if (and only if) they don't already exist. system(("mkdir -p " + nsdir ).data()); @@ -1708,7 +1740,7 @@ void preprocessClass( // names are computed as part of the "information" for the maps just mentioned. void preprocessFiles(InfoSpecs &specs) { - action("Preprocessing input files"); + action("\nPreprocessing input files..."); // files for (const std::string &file : specs.JSONFiles) { const nlohmann::json jmain = readJSONFile(file,true); @@ -1717,6 +1749,7 @@ void preprocessFiles(InfoSpecs &specs) for (const auto &cl : jmain.items()) preprocessClass(specs, nsname, cl); } + action("Done preprocessing."); } // preprocessFiles @@ -1794,7 +1827,15 @@ void getClass( const bool body = classRHS.contains(bodystr) && !classRHS[bodystr].is_null(); assert(!(data && body)); // not both per.isData = data || body; - per.dataType = data ? classRHS[datastr] : ""; + if (data) { + // A type change, as with metadata, could be wanted in this context as + // well. Perhaps the name "mapMetaType" (and the location and name for + // it in the changes.json file) should be modified to reflect this + const std::string type = classRHS[datastr]; + const auto it = specs.mapMetaType.find(type); + per.dataType = it == specs.mapMetaType.end() ? type : it->second; + } else + per.dataType = ""; // per.code will contain printed C++ code for the class itself writeClass(per,specs); @@ -1807,7 +1848,7 @@ void getClass( // getFiles void getFiles(InfoSpecs &specs) { - action("Creating classes"); + action("\nCreating classes..."); // files for (const std::string &file : specs.JSONFiles) { const nlohmann::json jmain = readJSONFile(file,true); @@ -1816,6 +1857,7 @@ void getFiles(InfoSpecs &specs) for (const auto &cl : jmain.items()) getClass(specs, nsname, cl); } + action("Done creating classes."); } // getFiles @@ -1867,8 +1909,8 @@ void fileGNDStkVersion(const InfoSpecs &specs) // Create an overarching file for this version writer out(specs.hppVersion); out(); - out("#ifndef NJOY_GNDSTK_@", allcaps(specs.VersionUnderscore)); - out("#define NJOY_GNDSTK_@", allcaps(specs.VersionUnderscore)); + out("#ifndef @_@", allcaps(specs.Project), allcaps(specs.VersionUnderscore)); + out("#define @_@", allcaps(specs.Project), allcaps(specs.VersionUnderscore)); std::string nsname_last = ""; for (auto &c : specs.class2data) { @@ -1877,41 +1919,18 @@ void fileGNDStkVersion(const InfoSpecs &specs) if (nsname != nsname_last) out(); nsname_last = nsname; - out("#include \"GNDStk/@/@/@.hpp\"", specs.Version, nsname, clname); + out("#include \"@/@/@/@.hpp\"", + specs.Project, specs.Version, nsname, clname); } out(); - out("#include \"GNDStk/@/key.hpp\"", specs.Version); + out("#include \"@/@/key.hpp\"", specs.Project, specs.Version); out(); out("#endif"); } // fileGNDStkVersion -// fixme Reconsider the arrangement described here... // fileGNDStkKey -const std::string file_key_comment = -R"***( -This file contains Meta and Child objects for metadata and child nodes in the -current GNDS version. These may prove to be useful if you wish to use the Core -Interface in conjunction with the autogenerated classes for this GNDS version. - -Within the outer njoy::GNDStk::version namespace below, the remaining namespace -arrangement was chosen to make the use of these objects smooth and logical. - -Meta and Child objects are collectively called "keys." Meta keys are placed -into key::meta. Child keys correspond to autogenerated classes, each of which -is already in some namespace; we thus use theNamespace::key::child::. That way, -an autogenerated class [ns::Foo] has [ns::key::foo] as its Child object, and -a "using namespace ns" allows the class and the Child object to be [Foo] and -[key::foo], respectively. (If we reordered ns:: and key::, that wouldn't work.) - -Within key::, we use meta:: and child:: around Meta and Child objects, just in -case there exist any identical GNDS metadata names and child-node names. (That -can, in fact, happen). The "using namespace meta" and "using namespace child" -directives then make the Meta<> and Child<> objects appear directly in key::, -so that "meta::" and "child::" are needed only to disambiguate identical names. -)***"; - void fileGNDStkKey(const InfoSpecs &specs) { // ------------------------ @@ -1958,16 +1977,17 @@ void fileGNDStkKey(const InfoSpecs &specs) writer out(specs.hppKey); out(); - out("/*",false); - out(file_key_comment,false); - out("*/"); - out(); - out("#ifndef NJOY_GNDSTK_@_KEY", allcaps(specs.VersionUnderscore)); - out("#define NJOY_GNDSTK_@_KEY", allcaps(specs.VersionUnderscore)); + out("#ifndef @_@_KEY", + allcaps(specs.Project), allcaps(specs.VersionUnderscore)); + out("#define @_@_KEY", + allcaps(specs.Project), allcaps(specs.VersionUnderscore)); out(); - out("namespace njoy {"); - out("namespace GNDStk {"); + if (specs.Project == "GNDStk") // <== use namespace njoy only for this + out("namespace njoy {"); + out("namespace @ {", specs.Project); out("namespace @ {", specs.VersionUnderscore); + out(); + out("using namespace njoy::GNDStk;"); // ------------------------ // Meta<> objects @@ -2032,8 +2052,9 @@ void fileGNDStkKey(const InfoSpecs &specs) out(largeComment); out(); out("} // namespace @", specs.VersionUnderscore); - out("} // namespace GNDStk"); - out("} // namespace njoy"); + out("} // namespace @", specs.Project); + if (specs.Project == "GNDStk") // <== end namespace njoy only for this + out("} // namespace njoy"); out(); out("#endif"); } // fileGNDStkKey @@ -2046,34 +2067,38 @@ void fileGNDStkClass( // class-specific hpp file writer out(per.hppGNDStk); const std::string guard = - "NJOY_GNDSTK_" + allcaps(specs.VersionUnderscore) + "_" + - allcaps(per.nsname) + "_" + allcaps(per.clname); + allcaps(specs.Project) + "_" + + allcaps(specs.VersionUnderscore) + "_" + + allcaps(per.nsname) + "_" + + allcaps(per.clname); out(); out("#ifndef @", guard); out("#define @", guard); out(); - out("// core interface"); + out("// GNDStk Core Interface"); out("#include \"GNDStk.hpp\""); if (c2d.dependencies.size() > 0) { out(); - out("// @ dependencies", specs.Version); + out("// Dependencies"); for (const auto &dep : c2d.dependencies) - out("#include \"GNDStk/@/@/@.hpp\"", - specs.Version, dep.nsname, dep.clname); + out("#include \"@/@/@/@.hpp\"", + specs.Project, specs.Version, dep.nsname, dep.clname); } out(); - out("namespace njoy {"); - out("namespace GNDStk {"); + if (specs.Project == "GNDStk") + out("namespace njoy {"); + out("namespace @ {", specs.Project); out("namespace @ {", specs.VersionUnderscore); out(); - out("using namespace njoy::GNDStk::core;"); + out("using namespace njoy::GNDStk;"); out(per.code,false); out("} // namespace @", specs.VersionUnderscore); - out("} // namespace GNDStk"); - out("} // namespace njoy"); + out("} // namespace @", specs.Project); + if (specs.Project == "GNDStk") + out("} // namespace njoy"); out(); out("#endif"); } // fileGNDStkClass @@ -2107,7 +2132,10 @@ void filePythonNamespace(const InfoSpecs &specs, const PerNamespace &per) out(1,"// create the @ submodule", per.nsname); out(1,"python::module submodule = module.def_submodule("); out(2,"\"@\",", per.nsname); - out(2,"\"GNDS @ @\"", specs.Version, per.nsname); + if (specs.Project == "GNDStk") + out(2,"\"GNDS @ @\"", specs.Version, per.nsname); // "GNDS", not "GNDStk" + else + out(2,"\"@ @ @\"", specs.Project, specs.Version, per.nsname); out(1,");"); out(); @@ -2196,7 +2224,8 @@ void filePythonClass(const InfoSpecs &specs, const PerClass &per) out(); out("// local includes"); - out("#include \"GNDStk/@/@/@.hpp\"", specs.Version, nsname, clname); + out("#include \"@/@/@/@.hpp\"", + specs.Project, specs.Version, nsname, clname); out("#include \"definitions.hpp\""); out(); @@ -2211,8 +2240,10 @@ void filePythonClass(const InfoSpecs &specs, const PerClass &per) out("// @ wrapper", clname); out("void wrap@(python::module &module)", clname); out("{"); - out(1,"using namespace njoy::GNDStk;"); - out(1,"using namespace njoy::GNDStk::@;", specs.VersionUnderscore); + const std::string prefix = specs.Project == "GNDStk" ? "njoy::" : ""; + out(1,"using namespace @@;", prefix, specs.Project); + out(1,"using namespace @@::@;", + prefix, specs.Project, specs.VersionUnderscore); out(); out(1,"// type aliases"); out(1,"using Component = @::@;", nsname, clname); @@ -2317,7 +2348,8 @@ void filePythonClass(const InfoSpecs &specs, const PerClass &per) for ( const auto& dataTypeName : dataTypesNames ) { out(2,".def_property_readonly("); out(3,"\"@\",", dataTypeName.second); - out(3,"[] (const Component &self) { return self.@(); },", dataTypeName.second); + out(3,"[] (const Component &self) { return self.@(); },", + dataTypeName.second); out(3,"Component::documentation(\"@\").data()", dataTypeName.second); out(2,")"); } diff --git a/autogen/prototype.json b/autogen/prototype.json index 856a88ec1..c09994388 100644 --- a/autogen/prototype.json +++ b/autogen/prototype.json @@ -1,20 +1,27 @@ { "comment" : [ - "GNDSDir", - " Base GNDStk directory into which the autogenerator tool", - " should place src/GNDStk/version.hpp and src/GNDStk/version/*", + "Path", + " Path to the Project directory.", + " Optional (defaults to .).", + "", + "Project", + " Base project directory into which the code generator should", + " place files. IMPORTANT: this should be a **plain** name only,", + " without any directory path. Use Path for that, if it's needed.", + " Optional (defaults to GNDStk).", "", "JSONDir", " Directory where the listed .json input files are located", "", "TO AUTOGENERATE THE PROTOTYPE IN THE REAL GNDStk HIERARCHY", - " Set GNDSDir to .. if you run json2class.exe from within", - " GNDStk/autogen/, where GNDStk/ is the cloned repository", + " Set Path to ../.. if you run json2class.exe from within", + " GNDStk/autogen/, where GNDStk/ is the cloned repository.", + " This (../..) is where GNDStk is located.", "", - "Do NOT end the directories (JSONDir and GNDSDir) with a /" + "Do NOT end directories with a /" ], - "GNDSDir": "..", + "Path": "../..", "Version": "v1.9", "JSONDir": "prototype", @@ -22,5 +29,6 @@ "generalPurpose.json", "reactionSuite.json" ], + "Changes": "changes.json" } diff --git a/autogen/v1.9.json b/autogen/v1.9.json index 87bde7140..fdc6ad62e 100644 --- a/autogen/v1.9.json +++ b/autogen/v1.9.json @@ -1,4 +1,7 @@ { + "GNDSDir": "test", + "Version": "v1.9", + "JSONDir": "v1.9", "JSONFiles": [ "summary_abstract.json", @@ -19,7 +22,5 @@ "summary_tsl.json" ], - "GNDSDir": "test", - "Version": "v1.9", "Changes": "changes.json" } diff --git a/cmake/GNDStk_dependencies.cmake b/cmake/GNDStk_dependencies.cmake index 46c409ac9..4e133f08e 100644 --- a/cmake/GNDStk_dependencies.cmake +++ b/cmake/GNDStk_dependencies.cmake @@ -10,28 +10,28 @@ include( FetchContent ) # Declare project dependencies ######################################################################## -FetchContent_Declare( catch-adapter - GIT_REPOSITORY http://github.com/njoy/catch-adapter - GIT_TAG origin/master - GIT_SHALLOW TRUE +FetchContent_Declare(catch-adapter + GIT_REPOSITORY http://github.com/njoy/catch-adapter + GIT_TAG origin/master + GIT_SHALLOW TRUE ) -FetchContent_Declare( Log - GIT_REPOSITORY http://github.com/njoy/Log - GIT_TAG origin/build/fetchcontent - GIT_SHALLOW TRUE +FetchContent_Declare(Log + GIT_REPOSITORY http://github.com/njoy/Log + GIT_TAG origin/master + GIT_SHALLOW TRUE ) -FetchContent_Declare( pugixml-adapter - GIT_REPOSITORY http://github.com/njoy/pugixml-adapter - GIT_TAG origin/master - GIT_SHALLOW TRUE +FetchContent_Declare(pugixml-adapter + GIT_REPOSITORY http://github.com/njoy/pugixml-adapter + GIT_TAG origin/master + GIT_SHALLOW TRUE ) FetchContent_Declare(json - GIT_REPOSITORY https://github.com/nlohmann/json.git - GIT_TAG v3.7.3 - GIT_SHALLOW true + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.7.3 + GIT_SHALLOW TRUE ) FetchContent_GetProperties(json) @@ -41,10 +41,19 @@ if(NOT json_POPULATED) add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) endif() -FetchContent_Declare( pybind11 - GIT_REPOSITORY https://github.com/pybind/pybind11 - GIT_TAG v2.6.1 - GIT_SHALLOW TRUE +FetchContent_Declare(hdf5 + GIT_REPOSITORY https://github.com/BlueBrain/HighFive.git + GIT_TAG v2.3.1 + GIT_SHALLOW TRUE + ) +set( HIGHFIVE_USE_BOOST OFF CACHE INTERNAL "" ) +set( HIGHFIVE_UNIT_TESTS OFF CACHE INTERNAL "" ) +set( HIGHFIVE_EXAMPLES OFF CACHE INTERNAL "" ) + +FetchContent_Declare(pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG v2.6.1 + GIT_SHALLOW TRUE ) @@ -57,5 +66,6 @@ FetchContent_MakeAvailable( Log pugixml-adapter json + hdf5 pybind11 ) diff --git a/cmake/unit_testing.cmake b/cmake/unit_testing.cmake index bf5bacd44..22fb67be0 100644 --- a/cmake/unit_testing.cmake +++ b/cmake/unit_testing.cmake @@ -10,7 +10,6 @@ enable_testing() # Unit testing directories ####################################################################### -add_subdirectory( src/GNDStk/test ) add_subdirectory( src/GNDStk/Defaulted/test ) add_subdirectory( src/GNDStk/Tree/test ) add_subdirectory( src/GNDStk/type2string/test ) @@ -20,13 +19,13 @@ add_subdirectory( src/GNDStk/string2type/test ) add_subdirectory( src/GNDStk/Meta/test ) add_subdirectory( src/GNDStk/XML/test ) add_subdirectory( src/GNDStk/JSON/test ) +add_subdirectory( src/GNDStk/HDF5/test ) add_subdirectory( src/GNDStk/Node/test ) -add_subdirectory( src/GNDStk/keyword/test ) add_subdirectory( src/GNDStk/Child/test ) add_subdirectory( src/GNDStk/convert/test ) add_subdirectory( src/GNDStk/utility/test ) add_subdirectory( src/GNDStk/Component/test ) -add_subdirectory( src/GNDStk/BodyText/test ) +add_subdirectory( src/GNDStk/BlockData/test ) add_subdirectory( src/GNDStk/precision/test ) add_subdirectory( src/GNDStk/enums/Encoding/test ) diff --git a/docs/primer.rst b/docs/primer.rst index 724fb8f2b..3f3c789b0 100644 --- a/docs/primer.rst +++ b/docs/primer.rst @@ -262,8 +262,6 @@ or can be a direct string: ``"xml"``, etc. A direct string is shorter and slightly easier to type -- but, if mistyped, would lead to a run-time error, not a compile-time error, if that matters to you in this simple context. -**HDF5 is not supported at this time!** Just XML and JSON. - *You should seldom, if ever, need to provide the second argument*. Absent the second argument, **GNDStk determines the file type automatically**, and we doubt that you'll have any objections to that. If you do choose provide the second @@ -276,9 +274,9 @@ as we attempt to read the file pursuant to the (incorrect) forced format. GNDStk uses the "file magic number," not the file name, to determine file type automatically. The file magic number really means the first byte, or bytes, -of the file. XML files always begin with a ``<`` character. HDF files (not -supported yet) begin with ASCII 137 and a few other specific bytes. If the -first byte is neither of those values, then GNDStk assumes JSON format. +of the file. XML files always begin with a ``<`` character. HDF5 files begin +with ASCII 137 and a few other specific bytes. If the first byte is neither +of those values, then GNDStk assumes JSON format. A nice thing about using the file magic number, not the file name, is that it works for ``std::istream``, for which a "file name" isn't even available. @@ -406,7 +404,7 @@ Several remarks are in order here. The comparison operator for ``Tree`` compares the two GNDS trees in an *order-agnostic* manner. GNDS fundamentally provides data in two places: nodes (think XML "elements") in its overall tree structure, and metadata (think XML -"attributes"). The GNDS standard does not, however, consider ordering to be +attributes). The GNDS standard does not, however, consider ordering to be important. One tree node's child nodes or metadata, anywhere or everywhere throughout the entire tree structure, could be reordered arbitrarily, but if each remains equivalent -- in the same respect that we consider two mathematical @@ -492,14 +490,28 @@ with what XML is able to represent. the node. For example, in HDF5 the attribute ``nodeIndex`` could be added to each child in a group. -For (1), GNDStk does the first suggested action: it groups all of a node's -attributes under a child node called ``attributes``. We consider that to be -cleaner than using an ``_attr`` suffix. - -For (2), GNDStk does exactly as illustrated: multiple elements of the same name -are suffixed with ``0``, ``1``, etc. And, then, a JSON name/value pair with the -name ``nodeName``, as suggested, is created in order to preserve the original -*unsuffixed* element's name. +For (1), GNDStk does the first suggested action (which we think is +somewhat cleaner than using an ``_attr`` suffix), but with a slight twist: +it groups a node's attributes under a child node called ``#attributes``. The +``#`` prefix is something we use in node names throughout GNDStk, if and where +the nodes so named represent special things -- with special meaning, and needing +special treatment. The extra character allows special nodes to be identified +easily, in both the GNDStk code base itself or in files produced by GNDStk. +Moreover, one could imagine that a future GNDS version might -- just might -- +have a normal node with the name ``attributes``, or with some other name that +we might wish to use for a special purpose. If and where such situations occur, +our use of a special prefix character allows for an unambiguous interpretation. +(As an aside: in principle, we'd have preferred to use the S-like dollar sign, +``$``, to inside S for "special" node. Unfortunately, ``$`` has a particular +meaning in the C++ regular-expression library. As such, ``$`` is unsuitable +for use in identifying special nodes, as our various node-finding capabilities +support the use of regular expressions.) + +For (2), GNDStk does as suggested, except again with the ``#`` terminology as +described above. Multiple elements of the same name are suffixed with ``0``, +``1``, etc. Then, a JSON name/value pair with the name ``#nodeName`` -- as +suggested, but with our ``#`` prefix -- is created. Its value preserves the +original element's name. For (3), GNDStk does nothing in particular right now. Our understanding of GNDS is that it's designed so that elements -- nodes -- can appear in any order. @@ -549,8 +561,8 @@ node. (Valid GNDS top-level nodes, per the standard, are ``reactionSuite``, about it for now. Naturally, GNDStk reverses the modifications when we *read* from a JSON file -into our internal format. Specifically: values in an ``attributes`` block are -transformed into metadata in the enclosing node, and values from ``nodeName`` +into our internal format. Specifically: values in an ``#attributes`` block are +transformed into metadata in the enclosing node, and values from ``#nodeName`` name/value pairs replace index-suffixed names. At this time, GNDStk provides no other options, such as the ``_attr`` suffix @@ -560,7 +572,7 @@ We're not aware, at the time of this writing, of the existence any official JSON-format GNDS files. If and when such files come into existence, and if such files use a different scheme than we do for addressing the issues described above, then we'll provide capabilities at least for reading those -files, and perhaps for writing them in that manner as well. +files, and perhaps for writing files in that manner as well. @@ -770,7 +782,7 @@ the top of the ``n-094_Pu_239.xml`` GNDS file: :language: xml Here, an outer ``evaluated`` node (XML "element") contains four metadata -key/value pairs (XML "attributes") and two child elements. The first child +key/value pairs (XML attributes) and two child elements. The first child element, ``temperature``, contains two metadata pairs but no further child nodes. The second child element, ``projectileEnergyDomain``, contains three metadata pairs but no further child nodes. diff --git a/docs/tutorial/employees.json b/docs/tutorial/employees.json index 37bb8442b..cdfd9ff8e 100644 --- a/docs/tutorial/employees.json +++ b/docs/tutorial/employees.json @@ -2,30 +2,30 @@ "employees": { "employee0": { "name": { - "attributes": { + "#attributes": { "first": "Doc", "last": "Jones" } }, - "nodeName": "employee" + "#nodeName": "employee" }, "employee1": { "name": { - "attributes": { + "#attributes": { "first": "Grumpy", "last": "Smith" } }, - "nodeName": "employee" + "#nodeName": "employee" }, "employee2": { "name": { - "attributes": { + "#attributes": { "first": "Happy", "last": "Earp" } }, - "nodeName": "employee" + "#nodeName": "employee" } } } \ No newline at end of file diff --git a/python/src/core/Node.python.cpp b/python/src/core/Node.python.cpp index ff2e7f377..05a6d677a 100644 --- a/python/src/core/Node.python.cpp +++ b/python/src/core/Node.python.cpp @@ -13,7 +13,7 @@ namespace core { void wrapNode( python::module& module ) { // type aliases - using Component = njoy::GNDStk::core::Node; + using Component = njoy::GNDStk::Node; using ConstRefComponent = std::reference_wrapper< const Component >; // create the core component diff --git a/python/src/definitions.hpp b/python/src/definitions.hpp index 230bd1d99..8e9b91e7a 100644 --- a/python/src/definitions.hpp +++ b/python/src/definitions.hpp @@ -37,7 +37,7 @@ void addStandardComponentDefinitions( PythonClass& component ) { "from_string", [] ( const std::string& string ) -> Component { - using namespace njoy::GNDStk::core; + using namespace njoy::GNDStk; Node node; node << string; @@ -45,7 +45,7 @@ void addStandardComponentDefinitions( PythonClass& component ) { return Component( node ); }, python::arg( "string" ), - "Read the component from an XML or json string\n\n" + "Read the component from an XML or JSON string\n\n" "An exception is raised if something goes wrong while reading the\n" "component\n\n" "Arguments:\n" @@ -56,7 +56,7 @@ void addStandardComponentDefinitions( PythonClass& component ) { "to_xml_string", [] ( const Component& self ) -> std::string { - using namespace njoy::GNDStk::core; + using namespace njoy::GNDStk; std::ostringstream out; XML( Node( self ) ).write( out, false ); @@ -72,7 +72,7 @@ void addStandardComponentDefinitions( PythonClass& component ) { "to_xml_file", [] ( const Component& self, const std::string& fileName ) { - using namespace njoy::GNDStk::core; + using namespace njoy::GNDStk; XML( Node( self ) ).write( fileName ); }, diff --git a/python/src/v1.9/transport/ReactionSuite.python.cpp b/python/src/v1.9/transport/ReactionSuite.python.cpp index 664310fc2..894e0e0d4 100644 --- a/python/src/v1.9/transport/ReactionSuite.python.cpp +++ b/python/src/v1.9/transport/ReactionSuite.python.cpp @@ -88,23 +88,6 @@ void wrapReactionSuite(python::module &module) python::overload_cast<>(&Component::reactions), Component::documentation("reactions").data() ) - .def_static( - - "from_file", - [] ( const std::string& filename ) -> Component { - - using namespace njoy::GNDStk::core; - Tree tree( filename ); - - return Component( tree( child::reactionSuite ) ); - }, - python::arg( "filename" ), - "Read a reaction suite from an XML or json file\n\n" - "An exception is raised if something goes wrong while reading the\n" - "component\n\n" - "Arguments:\n" - " filename the name of the file" - ) ; // add standard component definitions diff --git a/src/GNDStk.hpp b/src/GNDStk.hpp index c665be5ae..849190c67 100644 --- a/src/GNDStk.hpp +++ b/src/GNDStk.hpp @@ -8,6 +8,8 @@ #include "pugixml.hpp" #include "nlohmann/json.hpp" +#include +#include #include "Log.hpp" @@ -63,82 +65,37 @@ namespace GNDStk { // external-library wrappers #include "GNDStk/XML.hpp" #include "GNDStk/JSON.hpp" +#include "GNDStk/HDF5.hpp" // string/Node to/from type #include "GNDStk/string2type.hpp" #include "GNDStk/type2string.hpp" // Meta, Child, and related -#include "GNDStk/convert_t.hpp" #include "GNDStk/Meta.hpp" #include "GNDStk/Child.hpp" -#include "GNDStk/keyword.hpp" #include "GNDStk/or.hpp" // optional, with default #include "GNDStk/Defaulted.hpp" -// sets of Meta/Child -#include "GNDStk/basic.hpp" -#include "GNDStk/misc.hpp" - // Tree: primary constructs #include "GNDStk/Node.hpp" #include "GNDStk/Tree.hpp" -// fixme This really belongs with basic.hpp and misc.hpp above, but I had to -// move it here due to some ordering issues that arose when I de-templated -// Node and Tree. We'll deal with this later; it's an internal issue, not -// something that will affect users in any manner. -#include "GNDStk/common.hpp" - // Node to/from type #include "GNDStk/node2type.hpp" #include "GNDStk/type2node.hpp" -// xml/json/tree conversions +// Tree/XML/JSON/HDF5 conversions #include "GNDStk/convert.hpp" -// fixme See above fixme -namespace basic { using namespace common; } -namespace misc { using namespace common; } - // Miscellaneous support constructs for Standard Interface classes #include "GNDStk/Support.hpp" // Base classes for primary Standard Interface data classes -#include "GNDStk/BodyText.hpp" +#include "GNDStk/BlockData.hpp" #include "GNDStk/Component.hpp" - -// ------------------------ -// GNDStk "core interface" -// ------------------------ - -/* -In a user code, writing this: - - using namespace njoy::GNDStk::core; - -gives the same effect as writing this: - - using namespace njoy::GNDStk; - using namespace njoy::GNDStk::basic; - -Meaning: We consider our "core interface" to consist of everything in GNDStk:: -proper, plus our basic:: set of Meta and Child objects. Those are the ones with - type, so that they return metadata and nodes in their original tree form. -Note that basic:: itself brings in Meta and Child objects from its own nested -meta:: and child:: namespaces. Those are separate because there's a small amount -of overlap between allowable GNDS metadatum names and node names. With this -scheme, if a name you wish to use isn't one of the overlapping names, just use -it. If it is, then prefix with meta:: or child:: as necessary. -*/ - -namespace core { - using namespace GNDStk; - using namespace basic; -} - } // namespace GNDStk } // namespace njoy diff --git a/src/GNDStk/BodyText.hpp b/src/GNDStk/BlockData.hpp similarity index 68% rename from src/GNDStk/BodyText.hpp rename to src/GNDStk/BlockData.hpp index 7b963fd0c..6227ca216 100644 --- a/src/GNDStk/BodyText.hpp +++ b/src/GNDStk/BlockData.hpp @@ -2,20 +2,20 @@ // Printing-related colors. // todo Eventually, this probably belongs in a more context-agnostic // location, such as GNDStk's utility.hpp file or something like it. -#include "GNDStk/BodyText/src/colors.hpp" +#include "GNDStk/BlockData/src/colors.hpp" // Miscellaneous helper constructs. -#include "GNDStk/BodyText/src/detail.hpp" +#include "GNDStk/BlockData/src/detail.hpp" // ----------------------------------------------------------------------------- -// BodyText -// The case is specialized and has the fun stuff. This one needs -// just a bit of content, in order to facilitate uniform treatment of BodyText. +// BlockData +// The case is specialized and has the fun stuff. This one needs +// just a bit of content, in order to facilitate uniform treatment of BlockData. // ----------------------------------------------------------------------------- -template -class BodyText { +template +class BlockData { public: using VariantOfVectors = std::variant; using VariantOfScalars = std::variant; @@ -26,32 +26,32 @@ class BodyText { // ----------------------------------------------------------------------------- -// BodyText +// BlockData // // Designed to be flexible, smart, and safe. Does lots of checks, and, for the -// DATA == void case, can essentially re-form itself depending on what type of -// data someone tries to extract. +// DATATYPE == void case, can essentially re-form itself depending on what type +// of data someone tries to extract. // -// For efficiency in the DATA == void case, an application might want to copy -// to its own vector (e.g. auto myvec = mybodytext.get>()) +// For efficiency in the DATATYPE == void case, an application may want to copy +// to its own vector (e.g. auto myvec = myblockdata.get>()) // in order to do work on (or with) the vector there, before copying it back. // ----------------------------------------------------------------------------- -template -class BodyText { +template +class BlockData { public: - #include "GNDStk/BodyText/src/types.hpp" + #include "GNDStk/BlockData/src/types.hpp" // For convenience in various SFINAE and if-constexpr constructs - static inline constexpr bool runtime = detail::isVoid; + static inline constexpr bool runtime = detail::isVoid; template struct is_supported { static inline constexpr bool value = ( runtime && detail::isAlternative) || (!runtime && ( - std::is_constructible_v || - std::is_convertible_v + std::is_constructible_v || + std::is_convertible_v )); }; template @@ -71,15 +71,15 @@ class BodyText { // Vector of . // Mutable, so that we can defer processing of the raw string into // a vector until, and unless, a caller *asks* for the vector. - // This will be used if, and only if, DATA == void. + // This will be used if, and only if, DATATYPE == void. mutable VariantOfVectors variant; - // Vector of - // This will be used if, and only if, DATA != void. + // Vector of + // This will be used if, and only if, DATATYPE != void. // data_t is used in a few places where, without it, we'd create compilation // errors by using "void" in invalid ways. The "int" below is arbitrary - // essentially a placeholder; the following is only used when !runtime. - using data_t = std::conditional_t; + using data_t = std::conditional_t; mutable std::vector vector; public: @@ -87,23 +87,23 @@ class BodyText { // Parameters that affect interpretation of the raw string: // struct vars { length, start, valueType } // Includes public getters and setters for those. - // We won't use valueType if DATA != void. - #include "GNDStk/BodyText/src/params.hpp" + // We won't use valueType if DATATYPE != void. + #include "GNDStk/BlockData/src/params.hpp" // trim - // Flag: should the conversion of BodyText data back into textual data, + // Flag: should the conversion of BlockData data back into textual data, // in a Node, trim zeros from the start and end of the output? mutable bool trim = true; // Getters and setters for the raw string: - #include "GNDStk/BodyText/src/string.hpp" + #include "GNDStk/BlockData/src/string.hpp" // active() Active active() const { return act; } // clear() // Clears the vector, or the active vector alternative in the variant. - BodyText &clear() + BlockData &clear() { if constexpr (runtime) std::visit([](auto &&alt) { alt.clear(); }, variant); @@ -117,7 +117,7 @@ class BodyText { // size() // Returns the size of the vector, or of the active vector alternative in // the variant. Depending on what someone may or may not have done with the - // current BodyText object, size() might or might not reflect the values of + // current BlockData object, size() might or might not reflect the values of // length and/or start, or reflect the current contents of the raw string. std::size_t size() const { @@ -128,21 +128,21 @@ class BodyText { } // Various vector get() functions, and the type-specific doubles() etc. - #include "GNDStk/BodyText/src/get.hpp" + #include "GNDStk/BlockData/src/get.hpp" // Read/write data, from/to a Node - #include "GNDStk/BodyText/src/fromNode.hpp" - #include "GNDStk/BodyText/src/toNode.hpp" + #include "GNDStk/BlockData/src/fromNode.hpp" + #include "GNDStk/BlockData/src/toNode.hpp" // Write to ostream // Not to be confused with the process of writing data to a Node - #include "GNDStk/BodyText/src/write.hpp" + #include "GNDStk/BlockData/src/write.hpp" // Pull/push length/start/valueType from/to derived-class struct content - #include "GNDStk/BodyText/src/sync.hpp" + #include "GNDStk/BlockData/src/sync.hpp" // Assignment // From string or vector; the former == calling our raw string setter - #include "GNDStk/BodyText/src/assign.hpp" + #include "GNDStk/BlockData/src/assign.hpp" -}; // class BodyText +}; // class BlockData diff --git a/src/GNDStk/BodyText/src/assign.hpp b/src/GNDStk/BlockData/src/assign.hpp similarity index 84% rename from src/GNDStk/BodyText/src/assign.hpp rename to src/GNDStk/BlockData/src/assign.hpp index b1b975822..a64598e18 100644 --- a/src/GNDStk/BodyText/src/assign.hpp +++ b/src/GNDStk/BlockData/src/assign.hpp @@ -4,7 +4,7 @@ // Same effect as the string(new string) setter // ----------------------------------------------------------------------------- -BodyText &operator=(const std::string &str) +BlockData &operator=(const std::string &str) { return string(str); } @@ -17,15 +17,15 @@ BodyText &operator=(const std::string &str) /* DISCUSSION -Elsewhere, a BodyText object can be made from a GNDS node. From GNDS, we get +Elsewhere, a BlockData object can be made from a GNDS node. From GNDS, we get a text string (copied to the rawstring field), from which a vector of values can be created on an as-needed basis. A GNDS node might also give us any of length, start, and valueType, which, when pulling data from the Node, we'll use if they're there, or otherwise assume to be our defaults. -Here, we're allowing for an assignment BodyText = vector. For this assignment, +Here, we're allowing for an assignment BlockData = vector. For this assignment, the caller should send the full, complete vector of values that's appropriate -for the GNDS Node whose data this BodyText object is intended to represent. +for the GNDS Node whose data this BlockData object is intended to represent. Specifically: the vector should contain whatever leading and/or trailing 0s the full data vector is supposed to have. The length and start values are @@ -40,14 +40,14 @@ assigning here.) As for valueType, this function attempts to guess it from the vector's element type, and sets it to "", the empty string, if we don't recognize that type. -If a vector that's assigned from, here, is still in play when this BodyText's +If a vector that's assigned from, here, is still in play when this BlockData's data are written to a Node (see the toNode() function), then toNode() will recompute length and start automatically, based on the vector's beginning and ending content, if the "trim" flag is set. See toNode() for more information. */ template -std::enable_if_t, BodyText &> +std::enable_if_t, BlockData &> operator=(const std::vector &vec) { // set the raw string to "", because it's no longer considered meaningful @@ -61,13 +61,13 @@ operator=(const std::vector &vec) // assign vector if constexpr (runtime) variant = vec; - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) vector = vec; else { vector.clear(); vector.reserve(vec.size()); for (const T &element : vec) - vector.push_back(DATA(element)); + vector.push_back(DATATYPE(element)); } act = Active::vector; diff --git a/src/GNDStk/BodyText/src/colors.hpp b/src/GNDStk/BlockData/src/colors.hpp similarity index 98% rename from src/GNDStk/BodyText/src/colors.hpp rename to src/GNDStk/BlockData/src/colors.hpp index f8850fde7..0f0c0affb 100644 --- a/src/GNDStk/BodyText/src/colors.hpp +++ b/src/GNDStk/BlockData/src/colors.hpp @@ -80,7 +80,7 @@ inline std::string bracket = plain::yellow; inline std::string optional = plain::cyan; inline std::string defaulted = plain::blue; -// Values in nodes with "body text" +// Values in nodes with block data inline std::string value = plain::white; // Comments diff --git a/src/GNDStk/BodyText/src/detail.hpp b/src/GNDStk/BlockData/src/detail.hpp similarity index 71% rename from src/GNDStk/BodyText/src/detail.hpp rename to src/GNDStk/BlockData/src/detail.hpp index dd5053e9e..3fba96109 100644 --- a/src/GNDStk/BodyText/src/detail.hpp +++ b/src/GNDStk/BlockData/src/detail.hpp @@ -32,79 +32,66 @@ using decays_t = typename decays::type; // ----------------------------------------------------------------------------- // ------------------------ -// Helpers +// has_index // ------------------------ -// has_length -template -struct has_length - : std::false_type { }; - -template -struct has_length - : std::true_type { }; - -// has_start -template -struct has_start - : std::false_type { }; - -template -struct has_start - : std::true_type { }; - -// has_valueType -template -struct has_valueType - : std::false_type { }; - -template -struct has_valueType - : std::true_type { }; - -// has_index template struct has_index : std::false_type { }; template -struct has_index +struct has_index< + T, + decltype( + (void) + // Just using T{}.index() on the next line, like we do with other has_* + // classes above, can lead to an ambiguity between this specialization + // and the std::variant specialization below, arising from the fact that + // std::variant has an index() function. Hence the std::conditional_t. + std::conditional_t::value,void,T>{}.index(), + 0 + ) +> : std::true_type { }; +// for variant template struct has_index> { // for variant: does any alternative have index? static constexpr bool value = (has_index::value || ...); }; +// ------------------------ // has_label +// ------------------------ + template struct has_label : std::false_type { }; template -struct has_label +struct has_label< + T, + // std::variant doesn't have a label(), like it has an index(), but we'll + // do the same thing here, for has_label, as we do above for has_index. + // It's harmless, and if std::variant is ever given a label() function... + decltype((void)std::conditional_t::value,void,T>{}.label(),0) +> : std::true_type { }; +// for variant template struct has_label> { // for variant: does any alternative have label? static constexpr bool value = (has_label::value || ...); }; - // ------------------------ // Prefer these. // They apply std::decay, // and don't need ::value // ------------------------ -template -inline constexpr bool hasLength = has_length >::value; -template -inline constexpr bool hasStart = has_start >::value; -template -inline constexpr bool hasValueType = has_valueType>::value; template inline constexpr bool hasIndex = has_index >::value; template @@ -167,4 +154,30 @@ auto getBounds(const std::vector &vec) return bnd; } + + +// ----------------------------------------------------------------------------- +// colorize_*(text) +// ----------------------------------------------------------------------------- + +#define gndstkPaste(one,two) one ## two +#define gndstkColorFun(part) \ + inline std::string gndstkPaste(colorize_,part)(const std::string &text) \ + { \ + return GNDStk::color && colors::part != "" \ + ? colors::part + text + colors::reset \ + : text; \ + } + + // colorize_label() etc. + gndstkColorFun(label) + gndstkColorFun(colon) + gndstkColorFun(component) + gndstkColorFun(brace) + gndstkColorFun(bracket) + gndstkColorFun(comment) + +#undef gndstkColorFun +#undef gndstkPaste + } // namespace detail diff --git a/src/GNDStk/BlockData/src/fromNode.hpp b/src/GNDStk/BlockData/src/fromNode.hpp new file mode 100644 index 000000000..d23d9b5c0 --- /dev/null +++ b/src/GNDStk/BlockData/src/fromNode.hpp @@ -0,0 +1,39 @@ + +// ----------------------------------------------------------------------------- +// BlockData::fromNode(Node) +// ----------------------------------------------------------------------------- + +void fromNode(const Node &node) +{ + // length, start, and valueType might be present in the Node, but we won't + // fetch any of them here. Elsewhere, the current BlockData object should + // have its length, start, and valueType pulled from those respective values + // in an object of a class derived from Component, which in turn derives from + // BlockData. That object's content will have been pulled from the same Node. + // Here, we just get the Node's values: "plain character data" in XML terms. + + bool found = false; + rawstring = node.pcdata(found); + + if (!found) { + rawstring = ""; + + // Warning, re: why are we in BlockData if there's no block + // data? Perhaps the Node has a non-default length and/or start, so that + // the values are all supposed to be...zero. Until and unless we discover + // otherwise, however, we doubt that that would be the case, and will + // consider a Node's lack of plain character data, in the present context, + // to be something that merits a warning. + log::warning( + "Component marked as having block data, a.k.a. XML \"pcdata\" " + "(plain\ncharacter data), " + "but no such content was found in the GNDS node." + ); + log::member("BlockData::fromNode(Node, with name \"{}\")", node.name); + } + + // Above, we set the raw string. The following reflects this, so that the + // vector, or a vector in the variant, will be rebuilt from the raw string + // if and when a caller asks for it. + act = Active::string; +} diff --git a/src/GNDStk/BodyText/src/get.hpp b/src/GNDStk/BlockData/src/get.hpp similarity index 80% rename from src/GNDStk/BodyText/src/get.hpp rename to src/GNDStk/BlockData/src/get.hpp index c2ff03d81..e149376aa 100644 --- a/src/GNDStk/BodyText/src/get.hpp +++ b/src/GNDStk/BlockData/src/get.hpp @@ -5,7 +5,7 @@ /* ------------------------ -When DATA == void +When DATATYPE == void ------------------------ Case 1 @@ -42,28 +42,28 @@ Type-specific getters with specific names: For example, name == doubles when T == double. ------------------------ -When DATA != void +When DATATYPE != void ------------------------ Case 1 Return reference to [const] vector: get> const get> -T must == DATA. +T must == DATATYPE. Case 2 Return reference to [const] T: get(n) const get(n) -T must == DATA. +T must == DATATYPE. Case 3 -Return reference to [const] vector +Return reference to [const] vector get() const get() Case 4 -Return reference to [const] DATA: +Return reference to [const] DATATYPE: get(n) const operator[](n) const get(n) @@ -71,13 +71,14 @@ Return reference to [const] DATA: Case 5 Type-specific getters with a specific name: - const std::vector &name() const - std::vector &name() - const DATA &name(n) const - DATA &name(n) -For example, name == doubles if DATA == double. Unlike in the DATA == void case, -we won't have this set of functions for each of name == doubles, name == ints, -name == strings, etc., but only for the name that's appropriate for type DATA. + const std::vector &name() const + std::vector &name() + const DATATYPE &name(n) const + DATATYPE &name(n) +For example, name == doubles if DATATYPE == double. Unlike in the DATATYPE == +void case, we won't have this set of functions for each of name == doubles, +name == ints, name == strings, etc., but only for the name that's appropriate +for type DATATYPE. */ @@ -107,7 +108,7 @@ If active == vector: If the variant already contains a vector: Return it; we're done. - *** Under the correct and normal use of BodyText, *** + *** Under the correct and normal use of BlockData, *** *** this simple action will probably be the most common. *** Else: @@ -117,7 +118,7 @@ If active == vector: In the active == vector case, length, start, and valueType aren't considered to be relevant, and play no role. We consider those values to be meaningful -ONLY in relation to BodyText's raw string, and we deal with them here only +ONLY in relation to BlockData's raw string, and we deal with them here only if and when we make the vector from the raw string. That way, callers can access, manipulate, and even completely change the @@ -144,7 +145,7 @@ vector in the variant. Note that because the variant was declared to be mutable, we were indeed able to rebuild the vector if doing so was necessary. But we'll still return a *const* reference in that case, because the present object is conceptually const, and a caller shouldn't therefore be allowed to -modify the vector outside of BodyText's machinery. +modify the vector outside of BlockData's machinery. Of course we also have a non-const version, for a non-const *this. */ @@ -154,7 +155,7 @@ Of course we also have a non-const version, for a non-const *this. template std::enable_if_t< ( runtime && detail::isAlternative) || - (!runtime && std::is_same_v>), + (!runtime && std::is_same_v>), const VECTOR & > get() const { @@ -168,7 +169,7 @@ std::enable_if_t< if (active() == Active::string) { static const std::string context_rebuilding = - "BodyText::get>(), remade from raw string"; + "BlockData::get>(), remade from raw string"; // Completely rebuild the vector from the raw string, making use of // length, start, and valueType. @@ -261,8 +262,8 @@ std::enable_if_t< // and a call get>() was made, meaning that the caller // wants a vector. // - // BodyText is intended to store just one vector - one that represents - // values in a GNDS node that has "body text." We don't, and shouldn't, + // BlockData is intended to store just one vector - one that represents + // values in a GNDS node that has block data. We don't, and shouldn't, // try to juggle multiple vectors of different types. Therefore, we'll // attempt to convert the existing vector to one of the requested type, // then place the new vector into the variant (replacing the old one.) @@ -274,7 +275,7 @@ std::enable_if_t< log::info( "Re-forming vector of one type into vector of another type;\n" "was this intentional?"); - log::member("BodyText::get>()"); + log::member("BlockData::get>()"); // Initialize a new vector that will soon replace the old one VECTOR newVector; @@ -308,7 +309,7 @@ std::enable_if_t< template std::enable_if_t< ( runtime && detail::isAlternative) || - (!runtime && std::is_same_v>), + (!runtime && std::is_same_v>), VECTOR & > get() { @@ -321,18 +322,19 @@ std::enable_if_t< // 2. get(n) // ----------------------------------------------------------------------------- -// For DATA == void (so that we have a variants>): +// For DATATYPE == void (so that we have a variants>): // These trigger a complete rebuild of the vector, if it isn't already of type // vector for the given T. This is intentional, in order to provide maximum // flexibility. However, be aware of it, for the sake of efficiency! In general, -// when using a BodyText object, we recommend sticking with one underlying type. +// when using a BlockData object, we recommend sticking with one underlying +// type, not dynamically changing from one type to another. -// For DATA != void (so that we have a vector): -// T == DATA is required, so that returning an element of the vector will -// return a reference to T. (A constructibility/convertibility requirement that -// we have in other BodyText-related code thus needs to be more stringent here. -// We can't just be able to make a T from a DATA. Those must in fact be the same -// type, because we return a reference.) +// For DATATYPE != void (so that we have a vector): +// T == DATATYPE is required, so that returning an element of the +// vector will return a reference to T. (A constructibility/ +// convertibility requirement that we have in other BlockData-related code thus +// needs to be more stringent here. We can't just be able to make a T from a +// DATATYPE. They must in fact be the same type, because we return a reference.) // For both of the above cases: // If the string (not the variant or the vector) is active, then a rebuild from @@ -341,7 +343,7 @@ std::enable_if_t< // const template std::enable_if_t< - supported && (runtime || std::is_same_v), + supported && (runtime || std::is_same_v), const T & > get(const std::size_t n) const @@ -349,7 +351,7 @@ get(const std::size_t n) const try { return get>()[n]; } catch (...) { - log::member("BodyText::get({})", n); + log::member("BlockData::get({})", n); throw; } } @@ -357,7 +359,7 @@ get(const std::size_t n) const // non-const template std::enable_if_t< - supported && (runtime || std::is_same_v), + supported && (runtime || std::is_same_v), T & > get(const std::size_t n) @@ -369,21 +371,25 @@ get(const std::size_t n) // ----------------------------------------------------------------------------- // 3. get() -// If DATA == void, returns a variants>. -// If DATA != void, returns a vector<>. +// If DATATYPE == void, returns a variants>. +// If DATATYPE != void, returns a vector<>. // ----------------------------------------------------------------------------- // const std::conditional_t< runtime, const VariantOfVectors &, - const std::vector & + const std::vector & > get() const { if constexpr (runtime) { detail::MapStringType( valueType(), - [this](auto &&t) { get>>(); } + [this](auto &&t) + { + // clang seems to need this-> explicitly to *not* emit a warning + this->get>>(); + } ); // We can't return the specific variant alternative that was just put // in place; it depended on a run-time check. So, we return the whole @@ -391,7 +397,7 @@ std::conditional_t< return variant; } else { // Simpler, but we do still need a get (in case the *string* is active). - get>(); + get>(); return vector; } } @@ -400,14 +406,14 @@ std::conditional_t< std::conditional_t< runtime, VariantOfVectors &, - std::vector & + std::vector & > get() { return const_cast< std::conditional_t< runtime, VariantOfVectors &, - std::vector & + std::vector & > >(std::as_const(*this).get()); } @@ -417,11 +423,11 @@ std::conditional_t< // ----------------------------------------------------------------------------- // 4. get(n) // -// If DATA == void, returns a variant (by value, because the returned -// object must be made on-the-fly from our variants>). +// If DATATYPE == void, returns a variant (by value, because the +// returned object must be made on-the-fly from our variants>). // -// If DATA != void, returns a scalar of type [const] DATA (by reference, because -// it's available directly in our vector). +// If DATATYPE != void, returns a scalar of type [const] DATATYPE (by reference, +// because it's available directly in our vector). // ----------------------------------------------------------------------------- // ------------------------ @@ -446,7 +452,7 @@ std::conditional_t< return vector[n]; } } catch (...) { - log::member("BodyText::get({})", n); + log::member("BlockData::get({})", n); throw; } } @@ -466,24 +472,24 @@ std::conditional_t< // non-const // ------------------------ -// If DATA == void: +// If DATATYPE == void: // Not needed, because the const versions return by value. // -// If DATA != void: -// Meaningful, because returns are by reference in this (DATA != void) case. +// If DATATYPE != void: +// Meaningful, because returns are by reference in this (DATATYPE != void) case. // So, we'll enable non-const versions for this case only. -// In case anyone wonders, D (not just DATA) is needed below because SFINAE -// applies when template argument *deduction* is taking place. DATA is already -// fixed, by context - we're in BodyText - and thus it isn't being -// deduced here. Templating these (otherwise non-template) functions with an -// argument that defaults to DATA, then using that argument in the SFINAE, is -// a simple trick that makes the SFINAE work as intended. As for VOID, it's -// necessary in order for the following to be unambiguous with the template -// versions of get(n) that are defined elsewhere in this file. +// In case anyone wonders, D (not just DATATYPE) is needed below because SFINAE +// applies when template argument *deduction* is taking place. DATATYPE is +// already fixed, by context - we're in BlockData - and thus it +// isn't being deduced here. Templating these (otherwise non-template) functions +// with an argument that defaults to DATATYPE, then using that argument in the +// SFINAE, is a simple trick that makes the SFINAE work as intended. As for +// VOID, it's necessary in order for the following to be unambiguous with the +// template versions of get(n) that are defined elsewhere in this file. // get(n) -template +template std::enable_if_t && !detail::isVoid, data_t &> get(const std::size_t n) { @@ -491,13 +497,13 @@ get(const std::size_t n) get(); return vector[n]; } catch (...) { - log::member("BodyText::get({})", n); + log::member("BlockData::get({})", n); throw; } } // operator[](n) -template +template std::enable_if_t, data_t &> operator[](const std::size_t n) { @@ -520,25 +526,25 @@ operator[](const std::size_t n) #define GNDSTK_MAKE_GETTER(name,TYPE) \ \ - template \ + template \ std::enable_if_t< \ detail::isVoid || \ std::is_same_v, const std::vector & \ > name() const { return get>(); } \ \ - template \ + template \ std::enable_if_t< \ detail::isVoid || \ std::is_same_v, std::vector & \ > name() { return get>(); } \ \ - template \ + template \ std::enable_if_t< \ detail::isVoid || \ std::is_same_v, const TYPE & \ > name(const std::size_t n) const { return get(n); } \ \ - template \ + template \ std::enable_if_t< \ detail::isVoid || \ std::is_same_v, TYPE & \ diff --git a/src/GNDStk/BodyText/src/params.hpp b/src/GNDStk/BlockData/src/params.hpp similarity index 76% rename from src/GNDStk/BodyText/src/params.hpp rename to src/GNDStk/BlockData/src/params.hpp index c545310f1..ad15c1534 100644 --- a/src/GNDStk/BodyText/src/params.hpp +++ b/src/GNDStk/BlockData/src/params.hpp @@ -12,7 +12,7 @@ Quoted [slightly edited] from the official JSON specification files for GNDS: values that are not stored. This attribute should only be used when the sum of start and the number of listed values do not add to the total number of data values. This should only happen when there are - trailing zeros not listed in the body text. + trailing zeros not listed in the block data. start Default: 0 @@ -31,37 +31,39 @@ our GNDS Standard Interface code autogeneration tool produces. private: -// toNode() works with a conceptually const object but may update these to be +// toNode() works with a conceptually const object, but may update these to be // consistent with vector data; so, mutable. -mutable struct { +struct { // Any of these might or might not have appeared in a particular node that - // had body text. For uniformity, we have them all here, and with defaults. - std::size_t length = 0; - std::size_t start = 0; - std::string valueType = ""; + // had block data. For uniformity, we have them all here, and with defaults. + mutable std::size_t length = 0; + mutable std::size_t start = 0; + mutable std::string valueType = ""; } vars; // ----------------------------------------------------------------------------- // Getters +// Note: we intentionally return by (non-const!) reference, because the values +// in question are mutable. // ----------------------------------------------------------------------------- public: // length -std::size_t length() const +std::size_t &length() const { return vars.length; } // start -std::size_t start() const +std::size_t &start() const { return vars.start; } // valueType -const std::string &valueType() const +std::string &valueType() const { return vars.valueType; } @@ -74,7 +76,7 @@ const std::string &valueType() const // ----------------------------------------------------------------------------- // length -BodyText &length(const std::optional &opt) +BlockData &length(const std::optional &opt) { if (opt.has_value()) vars.length = opt.value(); @@ -82,7 +84,7 @@ BodyText &length(const std::optional &opt) } // start -BodyText &start(const std::optional &opt) +BlockData &start(const std::optional &opt) { if (opt.has_value()) vars.start = opt.value(); @@ -90,7 +92,7 @@ BodyText &start(const std::optional &opt) } // valueType -BodyText &valueType(const std::optional &opt) +BlockData &valueType(const std::optional &opt) { if (opt.has_value()) vars.valueType = opt.value(); diff --git a/src/GNDStk/BodyText/src/string.hpp b/src/GNDStk/BlockData/src/string.hpp similarity index 94% rename from src/GNDStk/BodyText/src/string.hpp rename to src/GNDStk/BlockData/src/string.hpp index 6ffef6fca..7b40d34e3 100644 --- a/src/GNDStk/BodyText/src/string.hpp +++ b/src/GNDStk/BlockData/src/string.hpp @@ -16,7 +16,7 @@ const std::string &string() const // string(new string) // Builder pattern: return *this, so callers can use this function smoothly // in conjunction with the setters for length, start, and valueType. -BodyText &string(const std::string &str) +BlockData &string(const std::string &str) { clear(); // <== the vector, because it's no longer considered meaningful rawstring = str; diff --git a/src/GNDStk/BlockData/src/sync.hpp b/src/GNDStk/BlockData/src/sync.hpp new file mode 100644 index 000000000..f73166935 --- /dev/null +++ b/src/GNDStk/BlockData/src/sync.hpp @@ -0,0 +1,22 @@ + +// pullFromDerived(derived) +// Make this BlockData's length, start, and valueType be consistent with any or +// all such parameters that exist in the given object. Remember that this class, +// BlockData, is a base of Component, which is a base of some other class. +template +void pullFromDerived(const DERIVED &derived) +{ + length (derived.length ()); + start (derived.start ()); + valueType(derived.valueType()); +} + +// pushToDerived(derived) +// The reverse of the above. +template +void pushToDerived(DERIVED &derived) const +{ + derived.length () = length (); + derived.start () = start (); + derived.valueType() = valueType(); +} diff --git a/src/GNDStk/BodyText/src/toNode.hpp b/src/GNDStk/BlockData/src/toNode.hpp similarity index 79% rename from src/GNDStk/BodyText/src/toNode.hpp rename to src/GNDStk/BlockData/src/toNode.hpp index 3f9dde5dc..cb2ea6cac 100644 --- a/src/GNDStk/BodyText/src/toNode.hpp +++ b/src/GNDStk/BlockData/src/toNode.hpp @@ -1,17 +1,16 @@ // ----------------------------------------------------------------------------- -// BodyText::toNode +// BlockData::toNode // This is called by Component's conversion-to-Node (not toNode()) function. // It's "toNode()" here, not a conversion, because we're simply writing text // that Component's full conversion-to-Node will place into the Node itself. // ----------------------------------------------------------------------------- -// Use either (1) the original raw string, or (2) the variant of vectors or the -// vector (depending on DATA ==/!= void), based on whether or not the string -// is active. length, start, and valueType might be computed too, in which case -// they're also changed in the derived class in order to keep things consistent. -template -void toNode(std::string &text, DERIVED &derived) const +// Use either (1) the original raw string, or (2) either the variant of vectors +// or the vector (depending on DATATYPE ==/!= void), based on whether or not the +// raw string is active. If a vector (not the raw string) is active, then we'll +// also compute length, start, and valueType. +void toNode(std::string &text) const { // Use the raw string? if (active() == Active::string) { @@ -22,11 +21,11 @@ void toNode(std::string &text, DERIVED &derived) const // Use the vector... const bool isStringVector = ( runtime && std::holds_alternative>(variant)) || - (!runtime && std::is_same_v); + (!runtime && std::is_same_v); if constexpr ( runtime || - (!runtime && std::is_same_v) + (!runtime && std::is_same_v) ) { // the run-time if's get() calls below won't // necessarily make sense without the above if-constexpr @@ -36,7 +35,7 @@ void toNode(std::string &text, DERIVED &derived) const (get(0) == "" || get(size()-1) == "") ) { log::warning( - "BodyText.toNode() called with BodyText " + "BlockData.toNode() called with BlockData " "trim flag == false, but active\n" "data are in a vector. Printing " "leading/trailing empty strings\n" @@ -59,8 +58,7 @@ void toNode(std::string &text, DERIVED &derived) const if constexpr (runtime) vars.valueType = detail::visitMapTypeString(variant); else - vars.valueType = detail::MapTypeString::value[0]; - pushToDerived(derived); + vars.valueType = detail::MapTypeString::value[0]; // Values std::ostringstream oss; diff --git a/src/GNDStk/BodyText/src/types.hpp b/src/GNDStk/BlockData/src/types.hpp similarity index 100% rename from src/GNDStk/BodyText/src/types.hpp rename to src/GNDStk/BlockData/src/types.hpp diff --git a/src/GNDStk/BlockData/src/write.hpp b/src/GNDStk/BlockData/src/write.hpp new file mode 100644 index 000000000..a3bc88b5d --- /dev/null +++ b/src/GNDStk/BlockData/src/write.hpp @@ -0,0 +1,82 @@ + +// ----------------------------------------------------------------------------- +// write +// To an ostream, generally as part of Component's prettyprinting. +// ----------------------------------------------------------------------------- + +std::ostream &write(std::ostream &os, const int level) const +{ + // If empty, don't even write a newline + if ((active() == Active::string && rawstring == "") || + (active() == Active::vector && size() == 0)) + return os; + + // Coloring? + const bool coloring = GNDStk::color && GNDStk::colors::value != ""; + + // ------------------------ + // If string is active + // ------------------------ + + if (active() == Active::string) { + // Write the string exactly as-is, without our column formatting + // or any indentation; then also write a newline + return coloring + ? os << colors::value << rawstring << colors::reset << std::endl + : os << rawstring << std::endl; + } + + // ------------------------ + // If vector is active + // ------------------------ + + // Indentation (string, with some number of spaces) + const std::string indent(GNDStk::indent*level,' '); + + const auto writeLambda = + [&os,&indent,coloring](auto &&alt) + { + using T = std::decay_t; + const std::size_t size = alt.size(); + const std::size_t end = (GNDStk::truncate < 0) + ? size + : std::min(size,std::size_t(GNDStk::truncate)); + + // Print, using our column formatting + for (std::size_t i = 0; i < end; ++i) { + const T &element = alt[i]; + + // value's whitespace prefix + i == 0 + ? os << indent // at the very beginning, or... + : GNDStk::columns <= 0 || + i % std::size_t(std::abs(GNDStk::columns)) != 0 + ? os << ' ' // still on the current line, or... + : os << '\n' << indent; // starting the next line + + // value + using namespace detail; + if (coloring) os << colors::value; + if constexpr (std::is_floating_point_v) + os << Precision{}.write(element); + else + os << element; + if (coloring) os << colors::reset; + }; + + // If applicable, print a message saying the data were truncated + if (end < size) { + if (end > 0) + os << '\n'; + os << indent << detail::colorize_comment( + "// truncated; total #values == " + std::to_string(size)); + } + }; + + if constexpr (runtime) + std::visit(writeLambda,variant); + else + writeLambda(vector); + + return os << std::endl; +} diff --git a/src/GNDStk/BodyText/test/BodyText.test.cpp b/src/GNDStk/BlockData/test/BlockData.test.cpp similarity index 76% rename from src/GNDStk/BodyText/test/BodyText.test.cpp rename to src/GNDStk/BlockData/test/BlockData.test.cpp index 09cb28cc8..ab5c35d60 100644 --- a/src/GNDStk/BodyText/test/BodyText.test.cpp +++ b/src/GNDStk/BlockData/test/BlockData.test.cpp @@ -4,22 +4,22 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- -// Scenario for DATA == void +// Scenario for DATATYPE == void // ----------------------------------------------------------------------------- -SCENARIO("Testing GNDStk BodyText with DATA == void") { - // Most BodyText functionality is tested in the individual test files. +SCENARIO("Testing GNDStk BlockData with DATATYPE == void") { + // Most BlockData functionality is tested in the individual test files. // There are just a few things we'll do here. - // Ensure that we can make const and non-const and BodyText - // objects. Note that BodyText has only a default constructor. + // Ensure that we can make const and non-const and BlockData + // objects. Note that BlockData has only a default constructor. - GIVEN("A const BodyText cbtextt") { - const BodyText cbtextt; + GIVEN("A const BlockData cbtextt") { + const BlockData cbtextt; THEN("It constructed correctly, and its data are as expected") { CHECK(cbtextt.length () == 0); CHECK(cbtextt.size () == 0); @@ -28,15 +28,15 @@ SCENARIO("Testing GNDStk BodyText with DATA == void") { } } - GIVEN("A const BodyText cbtextf") { - const BodyText cbtextf; + GIVEN("A const BlockData cbtextf") { + const BlockData cbtextf; THEN("It constructed correctly") { // no data for } } - GIVEN("A non-const BodyText nbtextt") { - BodyText nbtextt; + GIVEN("A non-const BlockData nbtextt") { + BlockData nbtextt; THEN("It constructed correctly, and its data are as expected") { CHECK(nbtextt.length () == 0); CHECK(nbtextt.size () == 0); @@ -45,21 +45,21 @@ SCENARIO("Testing GNDStk BodyText with DATA == void") { } } - GIVEN("A non-const BodyText nbtextf") { - BodyText nbtextf; + GIVEN("A non-const BlockData nbtextf") { + BlockData nbtextf; THEN("It constructed correctly") { // no data for } } // clear() and size() are defined (at the time of this writing) in the - // BodyText.hpp file itself, so we'll test them here. size() actually + // BlockData.hpp file itself, so we'll test them here. size() actually // was used in various tests, and thus was indirectly tested elsewhere. - GIVEN("A BodyText") { + GIVEN("A BlockData") { // clear WHEN("We test clear()") { - BodyText b; + BlockData b; // try int THEN("size() works correctly for vector") { @@ -88,7 +88,7 @@ SCENARIO("Testing GNDStk BodyText with DATA == void") { // size WHEN("We test size()") { - BodyText b; + BlockData b; // try int THEN("size() works correctly for vector") { @@ -125,13 +125,13 @@ SCENARIO("Testing GNDStk BodyText with DATA == void") { // ----------------------------------------------------------------------------- -// Scenario for DATA != void +// Scenario for DATATYPE != void // ----------------------------------------------------------------------------- -SCENARIO("Testing GNDStk BodyText with DATA != void") { +SCENARIO("Testing GNDStk BlockData with DATATYPE != void") { - GIVEN("A const BodyText cbtextt") { - const BodyText cbtextt; + GIVEN("A const BlockData cbtextt") { + const BlockData cbtextt; THEN("It constructed correctly, and its data are as expected") { CHECK(cbtextt.length () == 0); CHECK(cbtextt.size () == 0); @@ -140,15 +140,15 @@ SCENARIO("Testing GNDStk BodyText with DATA != void") { } } - GIVEN("A const BodyText cbtextf") { - const BodyText cbtextf; + GIVEN("A const BlockData cbtextf") { + const BlockData cbtextf; THEN("It constructed correctly") { // no data for } } - GIVEN("A non-const BodyText nbtextt") { - BodyText nbtextt; + GIVEN("A non-const BlockData nbtextt") { + BlockData nbtextt; THEN("It constructed correctly, and its data are as expected") { CHECK(nbtextt.length () == 0); CHECK(nbtextt.size () == 0); @@ -157,17 +157,17 @@ SCENARIO("Testing GNDStk BodyText with DATA != void") { } } - GIVEN("A non-const BodyText nbtextf") { - BodyText nbtextf; + GIVEN("A non-const BlockData nbtextf") { + BlockData nbtextf; THEN("It constructed correctly") { // no data for } } - GIVEN("A BodyText") { + GIVEN("A BlockData") { // clear WHEN("We test clear()") { - BodyText b; + BlockData b; THEN("size() works correctly") { b = std::vector{1,2,3,4,5}; CHECK(b.size() == 5); @@ -178,7 +178,7 @@ SCENARIO("Testing GNDStk BodyText with DATA != void") { // size WHEN("We test size()") { - BodyText b; + BlockData b; THEN("size() works correctly") { b = std::vector{"one","two","three","four","five"}; CHECK(b.size() == 5); diff --git a/src/GNDStk/BodyText/test/CMakeLists.txt b/src/GNDStk/BlockData/test/CMakeLists.txt similarity index 72% rename from src/GNDStk/BodyText/test/CMakeLists.txt rename to src/GNDStk/BlockData/test/CMakeLists.txt index 37cdf9076..c8916af70 100644 --- a/src/GNDStk/BodyText/test/CMakeLists.txt +++ b/src/GNDStk/BlockData/test/CMakeLists.txt @@ -1,6 +1,6 @@ -add_executable( GNDStk.BodyText.test - BodyText.test.cpp +add_executable( GNDStk.BlockData.test + BlockData.test.cpp assign.test.cpp detail.test.cpp fromNode.test.cpp @@ -11,7 +11,7 @@ add_executable( GNDStk.BodyText.test toNode.test.cpp types.test.cpp write.test.cpp ) -target_compile_options( GNDStk.BodyText.test PRIVATE ${${PREFIX}_common_flags} +target_compile_options( GNDStk.BlockData.test PRIVATE ${${PREFIX}_common_flags} $<$:${${PREFIX}_strict_flags}>$<$: ${${PREFIX}_DEBUG_flags} $<$:${${PREFIX}_coverage_flags}>> @@ -21,5 +21,5 @@ $<$:${${PREFIX}_link_time_optimization_flags}> $<$:${${PREFIX}_nonportable_optimization_flags}>> ${CXX_appended_flags} ${GNDStk_appended_flags} ) -target_link_libraries( GNDStk.BodyText.test PUBLIC GNDStk ) -add_test( NAME GNDStk.BodyText COMMAND GNDStk.BodyText.test ) +target_link_libraries( GNDStk.BlockData.test PUBLIC GNDStk ) +add_test( NAME GNDStk.BlockData COMMAND GNDStk.BlockData.test ) diff --git a/src/GNDStk/BodyText/test/assign.test.cpp b/src/GNDStk/BlockData/test/assign.test.cpp similarity index 88% rename from src/GNDStk/BodyText/test/assign.test.cpp rename to src/GNDStk/BlockData/test/assign.test.cpp index 7326ae81d..8da3e023f 100644 --- a/src/GNDStk/BodyText/test/assign.test.cpp +++ b/src/GNDStk/BlockData/test/assign.test.cpp @@ -2,20 +2,20 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- -// Scenario for DATA == void +// Scenario for DATATYPE == void // ----------------------------------------------------------------------------- -SCENARIO("BodyText assignment operators") { - GIVEN("A default-constructed BodyText object") { +SCENARIO("BlockData assignment operators") { + GIVEN("A default-constructed BlockData object") { // Default value of raw string is as expected WHEN("We examine the raw string") { THEN("It is as expected") { - BodyText b; + BlockData b; CHECK(b.string() == ""); } } @@ -23,7 +23,7 @@ SCENARIO("BodyText assignment operators") { // Assignment from string works WHEN("We assign from a string") { THEN("The raw string has the correct value, and vector size() == 0") { - BodyText b; + BlockData b; // to ensure it clears the vector below... b = std::vector(10); @@ -40,7 +40,7 @@ SCENARIO("BodyText assignment operators") { // Assignment from vector works WHEN("We assign from a vector") { THEN("The variant has the correct value, and raw string == \"\"") { - BodyText b; + BlockData b; // to ensure it clears the raw string etc. below... b = "foo bar"; @@ -70,7 +70,7 @@ SCENARIO("BodyText assignment operators") { // Assign from vector; should set valueType WHEN("We assign from a vector") { THEN("valueType is set correctly") { - BodyText b; + BlockData b; b.string("foo").valueType("unknown"); CHECK(b.valueType() == "unknown"); @@ -83,7 +83,7 @@ SCENARIO("BodyText assignment operators") { // Assign from vector; should set valueType WHEN("We assign from a vector") { THEN("valueType is set correctly") { - BodyText b; + BlockData b; b.string("foo").valueType("unknown"); CHECK(b.valueType() == "unknown"); @@ -96,7 +96,7 @@ SCENARIO("BodyText assignment operators") { // For now, non-{int,double} sets valueType == "" WHEN("We assign from a vector") { THEN("valueType is set correctly") { - BodyText b; + BlockData b; b.string("foo").valueType("unknown"); CHECK(b.valueType() == "unknown"); @@ -111,16 +111,16 @@ SCENARIO("BodyText assignment operators") { // ----------------------------------------------------------------------------- -// Scenario for DATA != void +// Scenario for DATATYPE != void // ----------------------------------------------------------------------------- -SCENARIO("BodyText assignment operators") { - GIVEN("A default-constructed BodyText object") { +SCENARIO("BlockData assignment operators") { + GIVEN("A default-constructed BlockData object") { // Default value of raw string is as expected WHEN("We examine the raw string") { THEN("It is as expected") { - BodyText b; + BlockData b; CHECK(b.string() == ""); } } @@ -128,7 +128,7 @@ SCENARIO("BodyText assignment operators") { // Assignment from string works WHEN("We assign from a string") { THEN("The raw string has the correct value, and vector size() == 0") { - BodyText b; + BlockData b; // to ensure it clears the vector below... b = std::vector(10); @@ -145,7 +145,7 @@ SCENARIO("BodyText assignment operators") { // Assignment from vector works WHEN("We assign from a vector") { THEN("The vector has the correct value, and raw string == \"\"") { - BodyText b; + BlockData b; // to ensure it clears the raw string etc. below... b = "foo bar"; @@ -175,7 +175,7 @@ SCENARIO("BodyText assignment operators") { // Assign from vector; should set valueType WHEN("We assign from a vector") { THEN("valueType is set correctly") { - BodyText b; + BlockData b; b.string("foo").valueType("unknown"); CHECK(b.valueType() == "unknown"); @@ -188,7 +188,7 @@ SCENARIO("BodyText assignment operators") { // Assign from vector; should set valueType WHEN("We assign from a vector") { THEN("valueType is set correctly") { - BodyText b; + BlockData b; b.string("foo").valueType("unknown"); CHECK(b.valueType() == "unknown"); @@ -201,7 +201,7 @@ SCENARIO("BodyText assignment operators") { // For now, non-{int,double} sets valueType == "" WHEN("We assign from a vector") { THEN("valueType is set correctly") { - BodyText b; + BlockData b; b.string("foo").valueType("unknown"); CHECK(b.valueType() == "unknown"); diff --git a/src/GNDStk/BodyText/test/detail.test.cpp b/src/GNDStk/BlockData/test/detail.test.cpp similarity index 70% rename from src/GNDStk/BodyText/test/detail.test.cpp rename to src/GNDStk/BlockData/test/detail.test.cpp index 5083a4f2e..593de9122 100644 --- a/src/GNDStk/BodyText/test/detail.test.cpp +++ b/src/GNDStk/BlockData/test/detail.test.cpp @@ -2,14 +2,14 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; inline std::string bar = "bar"; // ----------------------------------------------------------------------------- // Scenario // ----------------------------------------------------------------------------- -SCENARIO("Testing various BodyText-related detail:: constructs") { +SCENARIO("Testing various BlockData-related detail:: constructs") { // ------------------------ // scalarize @@ -73,122 +73,6 @@ SCENARIO("Testing various BodyText-related detail:: constructs") { } // GIVEN - // ------------------------ - // hasLength, hasStart, - // hasValueType - // ------------------------ - - GIVEN("Testing detail::hasLength, hasStart, and hasValueType") { - WHEN("A struct's content has length, start, and valueType") { - struct { - struct { - int length; - const double start = 0; - const std::string &valueType = bar; - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == true)); - CHECK((detail::hasStart == true)); - CHECK((detail::hasValueType == true)); - } - } - - WHEN("A struct's content has start and valueType") { - struct { - struct { - const double start = 0; - const std::string &valueType = bar; - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == false)); - CHECK((detail::hasStart == true)); - CHECK((detail::hasValueType == true)); - } - } - - WHEN("A struct's content has length and valueType") { - struct { - struct { - int length; - const std::string &valueType = bar; - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == true)); - CHECK((detail::hasStart == false)); - CHECK((detail::hasValueType == true)); - } - } - - WHEN("A struct's content has length and start") { - struct { - struct { - int length; - const double start = 0; - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == true)); - CHECK((detail::hasStart == true)); - CHECK((detail::hasValueType == false)); - } - } - - WHEN("A struct's content has length") { - struct { - struct { - int length; - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == true)); - CHECK((detail::hasStart == false)); - CHECK((detail::hasValueType == false)); - } - } - - WHEN("A struct's content has start") { - struct { - struct { - const double start = 0; - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == false)); - CHECK((detail::hasStart == true)); - CHECK((detail::hasValueType == false)); - } - } - - WHEN("A struct's content has valueType") { - struct { - struct { - const std::string &valueType = bar; - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == false)); - CHECK((detail::hasStart == false)); - CHECK((detail::hasValueType == true)); - } - } - - WHEN("A struct's content has none of length, start, or valueType") { - struct { - struct { - } content; - } foo; - THEN("Our SFINAE helpers detect this") { - CHECK((detail::hasLength == false)); - CHECK((detail::hasStart == false)); - CHECK((detail::hasValueType == false)); - } - } - } // GIVEN - - // ------------------------ // element2element // ------------------------ diff --git a/src/GNDStk/BodyText/test/fromNode.test.cpp b/src/GNDStk/BlockData/test/fromNode.test.cpp similarity index 72% rename from src/GNDStk/BodyText/test/fromNode.test.cpp rename to src/GNDStk/BlockData/test/fromNode.test.cpp index 25e6c5059..dda1bc292 100644 --- a/src/GNDStk/BodyText/test/fromNode.test.cpp +++ b/src/GNDStk/BlockData/test/fromNode.test.cpp @@ -2,19 +2,19 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- -// Scenario for DATA == void +// Scenario for DATATYPE == void // ----------------------------------------------------------------------------- -SCENARIO("BodyText fromNode()") { +SCENARIO("BlockData fromNode()") { - GIVEN("A Node with no \"body text\"") { - WHEN("BodyText.fromNode(the node) is called") { - THEN("The BodyText's raw string is \"\", as expected") { - BodyText b; + GIVEN("A Node with no \"block data\"") { + WHEN("BlockData.fromNode(the node) is called") { + THEN("The BlockData's raw string is \"\", as expected") { + BlockData b; b.string("This string should be replaced"); CHECK(b.string() != ""); @@ -26,10 +26,10 @@ SCENARIO("BodyText fromNode()") { } } - GIVEN("A Node with some \"body text\"") { - WHEN("BodyText.fromNode(the node) is called") { - THEN("The BodyText's raw string equals the text from the Node") { - BodyText b; + GIVEN("A Node with some \"block data\"") { + WHEN("BlockData.fromNode(the node) is called") { + THEN("The BlockData's raw string equals the text from the Node") { + BlockData b; b.string("This string should be replaced"); CHECK(b.string() != ""); @@ -61,15 +61,15 @@ SCENARIO("BodyText fromNode()") { // ----------------------------------------------------------------------------- -// Scenario for DATA != void +// Scenario for DATATYPE != void // ----------------------------------------------------------------------------- -SCENARIO("BodyText fromNode()") { +SCENARIO("BlockData fromNode()") { - GIVEN("A Node with no \"body text\"") { - WHEN("BodyText.fromNode(the node) is called") { - THEN("The BodyText's raw string is \"\", as expected") { - BodyText b; + GIVEN("A Node with no \"block data\"") { + WHEN("BlockData.fromNode(the node) is called") { + THEN("The BlockData's raw string is \"\", as expected") { + BlockData b; b.string("This string should be replaced"); CHECK(b.string() != ""); @@ -81,10 +81,10 @@ SCENARIO("BodyText fromNode()") { } } - GIVEN("A Node with some \"body text\"") { - WHEN("BodyText.fromNode(the node) is called") { - THEN("The BodyText's raw string equals the text from the Node") { - BodyText b; + GIVEN("A Node with some \"block data\"") { + WHEN("BlockData.fromNode(the node) is called") { + THEN("The BlockData's raw string equals the text from the Node") { + BlockData b; b.string("This string should be replaced"); CHECK(b.string() != ""); diff --git a/src/GNDStk/BodyText/test/get.test.cpp b/src/GNDStk/BlockData/test/get.test.cpp similarity index 68% rename from src/GNDStk/BodyText/test/get.test.cpp rename to src/GNDStk/BlockData/test/get.test.cpp index 4179ce9af..375a4d0f3 100644 --- a/src/GNDStk/BodyText/test/get.test.cpp +++ b/src/GNDStk/BlockData/test/get.test.cpp @@ -2,7 +2,7 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; @@ -33,33 +33,33 @@ void scenario_get_vector() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); + { BlockData b; b.start(0).length(0).string(""); CHECK((b.template get() == ivec{})); } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(4).string(""); CHECK((b.template get() == ivec{{0,0,0,0}})); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK((b.template get() == ivec{{0,0,0,0,0}})); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK((b.template get() == ivec{{0,0,0,0,0,0}})); } // 1 element in string - { BodyText b; b.start(0).length(0).string("-12"); + { BlockData b; b.start(0).length(0).string("-12"); CHECK((b.template get() == ivec(1,-12))); } - { BodyText b; b.start(0).length(4).string("-12"); + { BlockData b; b.start(0).length(4).string("-12"); CHECK((b.template get() == ivec{{-12,0,0,0}})); } - { BodyText b; b.start(1).length(5).string("-12"); + { BlockData b; b.start(1).length(5).string("-12"); CHECK((b.template get() == ivec{{0,-12,0,0,0}})); } - { BodyText b; b.start(2).length(6).string("-12"); + { BlockData b; b.start(2).length(6).string("-12"); CHECK((b.template get() == ivec{{0,0,-12,0,0,0}})); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("-12 34 -56"); + { BlockData b; b.start(0).length(0).string("-12 34 -56"); CHECK((b.template get() == ivec{{-12,34,-56}})); } - { BodyText b; b.start(0).length(4).string("-12 34 -56"); + { BlockData b; b.start(0).length(4).string("-12 34 -56"); CHECK((b.template get() == ivec{{-12,34,-56,0}})); } - { BodyText b; b.start(1).length(5).string("-12 34 -56"); + { BlockData b; b.start(1).length(5).string("-12 34 -56"); CHECK((b.template get() == ivec{{0,-12,34,-56,0}})); } - { BodyText b; b.start(2).length(6).string("-12 34 -56"); + { BlockData b; b.start(2).length(6).string("-12 34 -56"); CHECK((b.template get() == ivec{{0,0,-12,34,-56,0}})); } // ------------------------ @@ -67,33 +67,33 @@ void scenario_get_vector() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); + { BlockData b; b.start(0).length(0).string(""); CHECK((b.template get() == fvec{})); } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(4).string(""); CHECK((b.template get() == fvec{{0,0,0,0}})); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK((b.template get() == fvec{{0,0,0,0,0}})); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK((b.template get() == fvec{{0,0,0,0,0,0}})); } // 1 element in string - { BodyText b; b.start(0).length(0).string("1.2"); + { BlockData b; b.start(0).length(0).string("1.2"); CHECK((b.template get() == fvec(1,1.2))); } - { BodyText b; b.start(0).length(4).string("1.2"); + { BlockData b; b.start(0).length(4).string("1.2"); CHECK((b.template get() == fvec{{1.2,0,0,0}})); } - { BodyText b; b.start(1).length(5).string("1.2"); + { BlockData b; b.start(1).length(5).string("1.2"); CHECK((b.template get() == fvec{{0,1.2,0,0,0}})); } - { BodyText b; b.start(2).length(6).string("1.2"); + { BlockData b; b.start(2).length(6).string("1.2"); CHECK((b.template get() == fvec{{0,0,1.2,0,0,0}})); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("1.2 3.4 5.6"); + { BlockData b; b.start(0).length(0).string("1.2 3.4 5.6"); CHECK((b.template get() == fvec{{1.2,3.4,5.6}})); } - { BodyText b; b.start(0).length(4).string("1.2 3.4 5.6"); + { BlockData b; b.start(0).length(4).string("1.2 3.4 5.6"); CHECK((b.template get() == fvec{{1.2,3.4,5.6,0}})); } - { BodyText b; b.start(1).length(5).string("1.2 3.4 5.6"); + { BlockData b; b.start(1).length(5).string("1.2 3.4 5.6"); CHECK((b.template get() == fvec{{0,1.2,3.4,5.6,0}})); } - { BodyText b; b.start(2).length(6).string("1.2 3.4 5.6"); + { BlockData b; b.start(2).length(6).string("1.2 3.4 5.6"); CHECK((b.template get() == fvec{{0,0,1.2,3.4,5.6,0}})); } // ------------------------ @@ -101,33 +101,33 @@ void scenario_get_vector() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); + { BlockData b; b.start(0).length(0).string(""); CHECK((b.template get() == svec{})); } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(4).string(""); CHECK((b.template get() == svec{{"","","",""}})); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK((b.template get() == svec{{"","","","",""}})); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK((b.template get() == svec{{"","","","","",""}})); } // 1 element in string - { BodyText b; b.start(0).length(0).string("ab"); + { BlockData b; b.start(0).length(0).string("ab"); CHECK((b.template get() == svec(1,"ab"))); } - { BodyText b; b.start(0).length(4).string("ab"); + { BlockData b; b.start(0).length(4).string("ab"); CHECK((b.template get() == svec{{"ab","","",""}})); } - { BodyText b; b.start(1).length(5).string("ab"); + { BlockData b; b.start(1).length(5).string("ab"); CHECK((b.template get() == svec{{"","ab","","",""}})); } - { BodyText b; b.start(2).length(6).string("ab"); + { BlockData b; b.start(2).length(6).string("ab"); CHECK((b.template get() == svec{{"","","ab","","",""}})); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("ab cd ef"); + { BlockData b; b.start(0).length(0).string("ab cd ef"); CHECK((b.template get() == svec{{"ab","cd","ef"}})); } - { BodyText b; b.start(0).length(4).string("ab cd ef"); + { BlockData b; b.start(0).length(4).string("ab cd ef"); CHECK((b.template get() == svec{{"ab","cd","ef",""}})); } - { BodyText b; b.start(1).length(5).string("ab cd ef"); + { BlockData b; b.start(1).length(5).string("ab cd ef"); CHECK((b.template get() == svec{{"","ab","cd","ef",""}})); } - { BodyText b; b.start(2).length(6).string("ab cd ef"); + { BlockData b; b.start(2).length(6).string("ab cd ef"); CHECK((b.template get() == svec{{"","","ab","cd","ef",""}})); } // ------------------------ @@ -135,46 +135,46 @@ void scenario_get_vector() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); + { BlockData b; b.start(0).length(0).string(""); CHECK((b.template get() == uvec{})); } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(4).string(""); CHECK((b.template get() == uvec{{0,0,0,0}})); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK((b.template get() == uvec{{0,0,0,0,0}})); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK((b.template get() == uvec{{0,0,0,0,0,0}})); } // 1 element in string - { BodyText b; b.start(0).length(0).string("12"); + { BlockData b; b.start(0).length(0).string("12"); CHECK((b.template get() == uvec(1,12))); } - { BodyText b; b.start(0).length(4).string("12"); + { BlockData b; b.start(0).length(4).string("12"); CHECK((b.template get() == uvec{{12,0,0,0}})); } - { BodyText b; b.start(1).length(5).string("12"); + { BlockData b; b.start(1).length(5).string("12"); CHECK((b.template get() == uvec{{0,12,0,0,0}})); } - { BodyText b; b.start(2).length(6).string("12"); + { BlockData b; b.start(2).length(6).string("12"); CHECK((b.template get() == uvec{{0,0,12,0,0,0}})); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("12 34 56"); + { BlockData b; b.start(0).length(0).string("12 34 56"); CHECK((b.template get() == uvec{{12,34,56}})); } - { BodyText b; b.start(0).length(4).string("12 34 56"); + { BlockData b; b.start(0).length(4).string("12 34 56"); CHECK((b.template get() == uvec{{12,34,56,0}})); } - { BodyText b; b.start(1).length(5).string("12 34 56"); + { BlockData b; b.start(1).length(5).string("12 34 56"); CHECK((b.template get() == uvec{{0,12,34,56,0}})); } - { BodyText b; b.start(2).length(6).string("12 34 56"); + { BlockData b; b.start(2).length(6).string("12 34 56"); CHECK((b.template get() == uvec{{0,0,12,34,56,0}})); } } -// For BodyText -SCENARIO("BodyText get()") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get()") { + GIVEN("A BlockData object") { scenario_get_vector(); } } -// For BodyText -SCENARIO("BodyText get()") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get()") { + GIVEN("A BlockData object") { scenario_get_vector(); } } @@ -194,32 +194,32 @@ void scenario_get_template_n() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); /* no elements */ } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(0).string(""); } + { BlockData b; b.start(0).length(4).string(""); CHECK(b.template get(1) == 0); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK(b.template get(2) == 0); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK(b.template get(3) == 0); } // 1 element in string - { BodyText b; b.start(0).length(0).string("-12"); + { BlockData b; b.start(0).length(0).string("-12"); CHECK(b.template get(0) == -12); } - { BodyText b; b.start(0).length(4).string("-12"); + { BlockData b; b.start(0).length(4).string("-12"); CHECK(b.template get(1) == 0); } - { BodyText b; b.start(1).length(5).string("-12"); + { BlockData b; b.start(1).length(5).string("-12"); CHECK(b.template get(2) == 0); } - { BodyText b; b.start(2).length(6).string("-12"); + { BlockData b; b.start(2).length(6).string("-12"); CHECK(b.template get(3) == 0); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("-12 34 -56"); + { BlockData b; b.start(0).length(0).string("-12 34 -56"); CHECK(b.template get(0) == -12); } - { BodyText b; b.start(0).length(4).string("-12 34 -56"); + { BlockData b; b.start(0).length(4).string("-12 34 -56"); CHECK(b.template get(1) == 34); } - { BodyText b; b.start(1).length(5).string("-12 34 -56"); + { BlockData b; b.start(1).length(5).string("-12 34 -56"); CHECK(b.template get(2) == 34); } - { BodyText b; b.start(2).length(6).string("-12 34 -56"); + { BlockData b; b.start(2).length(6).string("-12 34 -56"); CHECK(b.template get(3) == 34); } // ------------------------ @@ -227,32 +227,32 @@ void scenario_get_template_n() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); /* no elements */ } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(0).string(""); } + { BlockData b; b.start(0).length(4).string(""); CHECK(b.template get(1) == 0); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK(b.template get(2) == 0); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK(b.template get(3) == 0); } // 1 element in string - { BodyText b; b.start(0).length(0).string("1.2"); + { BlockData b; b.start(0).length(0).string("1.2"); CHECK(b.template get(0) == 1.2); } - { BodyText b; b.start(0).length(4).string("1.2"); + { BlockData b; b.start(0).length(4).string("1.2"); CHECK(b.template get(1) == 0); } - { BodyText b; b.start(1).length(5).string("1.2"); + { BlockData b; b.start(1).length(5).string("1.2"); CHECK(b.template get(2) == 0); } - { BodyText b; b.start(2).length(6).string("1.2"); + { BlockData b; b.start(2).length(6).string("1.2"); CHECK(b.template get(3) == 0); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("1.2 3.4 5.6"); + { BlockData b; b.start(0).length(0).string("1.2 3.4 5.6"); CHECK(b.template get(0) == 1.2); } - { BodyText b; b.start(0).length(4).string("1.2 3.4 5.6"); + { BlockData b; b.start(0).length(4).string("1.2 3.4 5.6"); CHECK(b.template get(1) == 3.4); } - { BodyText b; b.start(1).length(5).string("1.2 3.4 5.6"); + { BlockData b; b.start(1).length(5).string("1.2 3.4 5.6"); CHECK(b.template get(2) == 3.4); } - { BodyText b; b.start(2).length(6).string("1.2 3.4 5.6"); + { BlockData b; b.start(2).length(6).string("1.2 3.4 5.6"); CHECK(b.template get(3) == 3.4); } // ------------------------ @@ -260,32 +260,32 @@ void scenario_get_template_n() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); /* no elements */ } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(0).string(""); } + { BlockData b; b.start(0).length(4).string(""); CHECK(b.template get(1) == ""); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK(b.template get(2) == ""); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK(b.template get(3) == ""); } // 1 element in string - { BodyText b; b.start(0).length(0).string("ab"); + { BlockData b; b.start(0).length(0).string("ab"); CHECK(b.template get(0) == "ab"); } - { BodyText b; b.start(0).length(4).string("ab"); + { BlockData b; b.start(0).length(4).string("ab"); CHECK(b.template get(1) == ""); } - { BodyText b; b.start(1).length(5).string("ab"); + { BlockData b; b.start(1).length(5).string("ab"); CHECK(b.template get(2) == ""); } - { BodyText b; b.start(2).length(6).string("ab"); + { BlockData b; b.start(2).length(6).string("ab"); CHECK(b.template get(3) == ""); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("ab cd ef"); + { BlockData b; b.start(0).length(0).string("ab cd ef"); CHECK(b.template get(0) == "ab"); } - { BodyText b; b.start(0).length(4).string("ab cd ef"); + { BlockData b; b.start(0).length(4).string("ab cd ef"); CHECK(b.template get(1) == "cd"); } - { BodyText b; b.start(1).length(5).string("ab cd ef"); + { BlockData b; b.start(1).length(5).string("ab cd ef"); CHECK(b.template get(2) == "cd"); } - { BodyText b; b.start(2).length(6).string("ab cd ef"); + { BlockData b; b.start(2).length(6).string("ab cd ef"); CHECK(b.template get(3) == "cd"); } // ------------------------ @@ -293,45 +293,45 @@ void scenario_get_template_n() // ------------------------ // 0 elements in string - { BodyText b; b.start(0).length(0).string(""); /* no elements */ } - { BodyText b; b.start(0).length(4).string(""); + { BlockData b; b.start(0).length(0).string(""); } + { BlockData b; b.start(0).length(4).string(""); CHECK(b.template get(1) == 0); } - { BodyText b; b.start(1).length(5).string(""); + { BlockData b; b.start(1).length(5).string(""); CHECK(b.template get(2) == 0); } - { BodyText b; b.start(2).length(6).string(""); + { BlockData b; b.start(2).length(6).string(""); CHECK(b.template get(3) == 0); } // 1 element in string - { BodyText b; b.start(0).length(0).string("12"); + { BlockData b; b.start(0).length(0).string("12"); CHECK(b.template get(0) == 12); } - { BodyText b; b.start(0).length(4).string("12"); + { BlockData b; b.start(0).length(4).string("12"); CHECK(b.template get(1) == 0); } - { BodyText b; b.start(1).length(5).string("12"); + { BlockData b; b.start(1).length(5).string("12"); CHECK(b.template get(2) == 0); } - { BodyText b; b.start(2).length(6).string("12"); + { BlockData b; b.start(2).length(6).string("12"); CHECK(b.template get(3) == 0); } // 3 elements in string - { BodyText b; b.start(0).length(0).string("12 34 56"); + { BlockData b; b.start(0).length(0).string("12 34 56"); CHECK(b.template get(0) == 12); } - { BodyText b; b.start(0).length(4).string("12 34 56"); + { BlockData b; b.start(0).length(4).string("12 34 56"); CHECK(b.template get(1) == 34); } - { BodyText b; b.start(1).length(5).string("12 34 56"); + { BlockData b; b.start(1).length(5).string("12 34 56"); CHECK(b.template get(2) == 34); } - { BodyText b; b.start(2).length(6).string("12 34 56"); + { BlockData b; b.start(2).length(6).string("12 34 56"); CHECK(b.template get(3) == 34); } } -// For BodyText -SCENARIO("BodyText get(n)") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get(n)") { + GIVEN("A BlockData object") { scenario_get_template_n(); } } -// For BodyText -SCENARIO("BodyText get(n)") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get(n)") { + GIVEN("A BlockData object") { scenario_get_template_n(); } } @@ -342,15 +342,15 @@ SCENARIO("BodyText get(n)") { // 3. Scenario: get() // ----------------------------------------------------------------------------- -// For BodyText -SCENARIO("BodyText get()") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get()") { + GIVEN("A BlockData object") { using ivec = std::vector; using fvec = std::vector; using svec = std::vector; - BodyText b; + BlockData b; b.start(2).length(6).string("-12 34 -56"); b.valueType("Integer32"); @@ -377,28 +377,28 @@ SCENARIO("BodyText get()") { } // SCENARIO -// For BodyText -SCENARIO("BodyText get()") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get()") { + GIVEN("A BlockData object") { using ivec = std::vector; using fvec = std::vector; using svec = std::vector; { - BodyText b; + BlockData b; b.start(2).length(6).string("-12 34 -56"); CHECK((b.get() == ivec{{0,0,-12,34,-56,0}})); } { - BodyText b; + BlockData b; b.start(2).length(6).string("1.2 3.4 5.6"); CHECK((b.get() == fvec{{0,0,1.2,3.4,5.6,0}})); } { - BodyText b; + BlockData b; b.start(2).length(6).string("ab cd ef"); CHECK((b.get() == svec{{"","","ab","cd","ef",""}})); } @@ -412,11 +412,11 @@ SCENARIO("BodyText get()") { // 4. Scenario: get(n) // ----------------------------------------------------------------------------- -// For BodyText -SCENARIO("BodyText get(n)") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get(n)") { + GIVEN("A BlockData object") { - BodyText b; + BlockData b; // ------------------------ // get(n) form @@ -514,16 +514,16 @@ SCENARIO("BodyText get(n)") { } // SCENARIO -// For BodyText -SCENARIO("BodyText get(n)") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData get(n)") { + GIVEN("A BlockData object") { // ------------------------ // get(n) form // ------------------------ { - BodyText b; + BlockData b; b.start(2).length(6).string("-12 34 -56"); CHECK(( b.get(0) == 0 )); CHECK(( b.get(1) == 0 )); @@ -534,7 +534,7 @@ SCENARIO("BodyText get(n)") { } { - BodyText b; + BlockData b; b.start(2).length(6).string("1.2 3.4 5.6"); CHECK(( b.get(0) == 0 )); CHECK(( b.get(1) == 0 )); @@ -545,7 +545,7 @@ SCENARIO("BodyText get(n)") { } { - BodyText b; + BlockData b; b.start(2).length(8).string("ab cd ef 123 4.5"); CHECK(( b.get(0) == "" )); CHECK(( b.get(1) == "" )); @@ -562,7 +562,7 @@ SCENARIO("BodyText get(n)") { // ------------------------ { - BodyText b; + BlockData b; b.start(2).length(6).string("-12 34 -56"); CHECK(( b[0] == 0 )); CHECK(( b[1] == 0 )); @@ -573,7 +573,7 @@ SCENARIO("BodyText get(n)") { } { - BodyText b; + BlockData b; b.start(2).length(6).string("1.2 3.4 5.6"); CHECK(( b[0] == 0 )); CHECK(( b[1] == 0 )); @@ -584,7 +584,7 @@ SCENARIO("BodyText get(n)") { } { - BodyText b; + BlockData b; b.start(2).length(6).string("ab cd ef"); CHECK(( b[0] == "" )); CHECK(( b[1] == "" )); @@ -608,7 +608,7 @@ template void scenario_get_named() { { - BodyText b; + BlockData b; b.start(2).length(6).string("-12 34 -56"); auto result = b.ints(); @@ -631,10 +631,10 @@ void scenario_get_named() } { - BodyText b; + BlockData b; b.start(2).length(6).string("1.2 3.4 5.6"); - const BodyText &bconst = b; // ensure it works with const + const BlockData &bconst = b; // ensure it works with const const auto result = bconst.doubles(); CHECK((std::is_same_v>)); @@ -655,7 +655,7 @@ void scenario_get_named() } { - BodyText b; + BlockData b; b.start(2).length(6).string("ab cd ef"); const auto &result = b.strings(); @@ -678,16 +678,18 @@ void scenario_get_named() } } -// For BodyText -SCENARIO("BodyText type-specific get functions: doubles() etc.") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData type-specific get functions: " + "doubles() etc.") { + GIVEN("A BlockData object") { scenario_get_named(); } } -// For BodyText -SCENARIO("BodyText type-specific get functions: doubles() etc.") { - GIVEN("A BodyText object") { +// For BlockData +SCENARIO("BlockData type-specific get functions: " + "doubles() etc.") { + GIVEN("A BlockData object") { scenario_get_named(); } } diff --git a/src/GNDStk/BodyText/test/params.test.cpp b/src/GNDStk/BlockData/test/params.test.cpp similarity index 81% rename from src/GNDStk/BodyText/test/params.test.cpp rename to src/GNDStk/BlockData/test/params.test.cpp index 7e43e3e32..7f6df6682 100644 --- a/src/GNDStk/BodyText/test/params.test.cpp +++ b/src/GNDStk/BlockData/test/params.test.cpp @@ -2,20 +2,20 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- // Helper // ----------------------------------------------------------------------------- -template +template void scenario_params() { // Default values of parameters are as expected WHEN("We examine the default length, start, and valueType") { THEN("They are as expected") { - BodyText b; + BlockData b; CHECK(b.length() == 0); CHECK(b.start() == 0); CHECK(b.valueType() == ""); @@ -25,17 +25,17 @@ void scenario_params() // length setter/getter works WHEN("We set length, then get and verify") { THEN("It works for a plain value") { - BodyText b; + BlockData b; b.length(12); CHECK(b.length() == 12); } THEN("It works for optional-with-value") { - BodyText b; + BlockData b; b.length(std::optional(34)); CHECK(b.length() == 34); } THEN("It works for optional-without-value (remains unchanged)") { - BodyText b; + BlockData b; b.length(56); b.length(std::optional(std::nullopt)); CHECK(b.length() == 56); @@ -45,17 +45,17 @@ void scenario_params() // start setter/getter works WHEN("We set start, then get and verify") { THEN("It works for a plain value") { - BodyText b; + BlockData b; b.start(11); CHECK(b.start() == 11); } THEN("It works for optional-with-value") { - BodyText b; + BlockData b; b.start(std::optional(13)); CHECK(b.start() == 13); } THEN("It works for optional-without-value (remains unchanged)") { - BodyText b; + BlockData b; b.start(17); b.start(std::optional(std::nullopt)); CHECK(b.start() == 17); @@ -65,17 +65,17 @@ void scenario_params() // valueType setter/getter works WHEN("We set valueType, then get and verify") { THEN("It works for a plain value") { - BodyText b; + BlockData b; b.valueType("unknown"); CHECK(b.valueType() == "unknown"); } THEN("It works for optional-with-value") { - BodyText b; + BlockData b; b.valueType(std::optional("Integer32")); CHECK(b.valueType() == "Integer32"); } THEN("It works for optional-without-value (remains unchanged)") { - BodyText b; + BlockData b; b.valueType("Float64"); b.valueType(std::optional(std::nullopt)); CHECK(b.valueType() == "Float64"); @@ -85,7 +85,7 @@ void scenario_params() // Combo of the above, using builder-pattern nature of the setters WHEN("We set length/start/valueType together, then get and verify") { THEN("It works for a plain value") { - BodyText b; + BlockData b; b.length(1) .start(2) .valueType("a"); @@ -94,7 +94,7 @@ void scenario_params() CHECK(b.valueType() == "a"); } THEN("It works for optional-with-value") { - BodyText b; + BlockData b; b.length(std::optional(3)) .start(std::optional(4)) .valueType(std::optional("b")); @@ -103,7 +103,7 @@ void scenario_params() CHECK(b.valueType() == "b"); } THEN("It works for optional-without-value (remains unchanged)") { - BodyText b; + BlockData b; b.length(100).start(200).valueType("c"); b.length(std::optional(std::nullopt)) .start(std::optional(std::nullopt)) @@ -120,14 +120,14 @@ void scenario_params() // Scenarios // ----------------------------------------------------------------------------- -SCENARIO("BodyText length/start/valueType") { - GIVEN("A default-constructed BodyText object") { +SCENARIO("BlockData length/start/valueType") { + GIVEN("A default-constructed BlockData object") { scenario_params(); } } -SCENARIO("BodyText length/start/valueType") { - GIVEN("A default-constructed BodyText object") { +SCENARIO("BlockData length/start/valueType") { + GIVEN("A default-constructed BlockData object") { scenario_params(); } } diff --git a/src/GNDStk/BodyText/test/string.test.cpp b/src/GNDStk/BlockData/test/string.test.cpp similarity index 79% rename from src/GNDStk/BodyText/test/string.test.cpp rename to src/GNDStk/BlockData/test/string.test.cpp index 549b38041..fb8e8ed39 100644 --- a/src/GNDStk/BodyText/test/string.test.cpp +++ b/src/GNDStk/BlockData/test/string.test.cpp @@ -2,20 +2,20 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- // Helper // ----------------------------------------------------------------------------- -template +template void scenario_string() { // Default value of raw string is as expected WHEN("We examine the raw string") { THEN("It is as expected") { - BodyText b; + BlockData b; CHECK(b.string() == ""); } } @@ -23,7 +23,7 @@ void scenario_string() // Raw string setter/getter works WHEN("We set the raw string") { THEN("It has the correct value, and vector size() == 0 too") { - BodyText b; + BlockData b; // to ensure it clears below... b = std::vector(10); @@ -40,7 +40,7 @@ void scenario_string() // Test in conjunction with length, start, and valueType WHEN("We set string, length, start, and valueType together") { THEN("All values check out") { - BodyText b; + BlockData b; b.string("3 4 5 6").length(10).start(2).valueType("Integer32"); CHECK(b.length() == 10); @@ -56,14 +56,14 @@ void scenario_string() // Scenarios // ----------------------------------------------------------------------------- -SCENARIO("BodyText string()") { - GIVEN("A default-constructed BodyText object") { +SCENARIO("BlockData string()") { + GIVEN("A default-constructed BlockData object") { scenario_string(); } } -SCENARIO("BodyText string()") { - GIVEN("A default-constructed BodyText object") { +SCENARIO("BlockData string()") { + GIVEN("A default-constructed BlockData object") { scenario_string(); } } diff --git a/src/GNDStk/BodyText/test/sync.test.cpp b/src/GNDStk/BlockData/test/sync.test.cpp similarity index 52% rename from src/GNDStk/BodyText/test/sync.test.cpp rename to src/GNDStk/BlockData/test/sync.test.cpp index 24df290c7..c4e4ac81c 100644 --- a/src/GNDStk/BodyText/test/sync.test.cpp +++ b/src/GNDStk/BlockData/test/sync.test.cpp @@ -2,7 +2,7 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- @@ -11,17 +11,17 @@ using namespace njoy::GNDStk::core; // ----------------------------------------------------------------------------- // Helper -template +template void scenario_pull() { WHEN("pullFromDerived() is called") { // none of length, start, valueType THEN("Push to content{} works") { - const struct { + struct : public BlockData { struct { } content; } derived; - BodyText b; + BlockData &b = derived; b.length(100).start(200).valueType("300"); b.pullFromDerived(derived); // should do nothing here CHECK(b.length() == 100); @@ -31,12 +31,14 @@ void scenario_pull() // length only THEN("Push to content{length} works") { - const struct { + struct : public BlockData { struct { int length = 10; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } } derived; - BodyText b; + BlockData &b = derived; b.length(11).start(12).valueType("13"); b.pullFromDerived(derived); CHECK(b.length() == 10); @@ -46,12 +48,14 @@ void scenario_pull() // start only THEN("Push to content{start} works") { - const struct { + struct : public BlockData { struct { int start = 14; } content; + const int &start() const { return content.start; } + int &start() { return content.start; } } derived; - BodyText b; + BlockData &b = derived; b.length(15).start(16).valueType("17"); b.pullFromDerived(derived); CHECK(b.length() == 15); @@ -61,12 +65,14 @@ void scenario_pull() // valueType only THEN("Push to content{valueType} works") { - const struct { + struct : public BlockData { struct { std::string valueType = "18"; } content; + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(19).start(20).valueType("21"); b.pullFromDerived(derived); CHECK(b.length() == 19); @@ -76,13 +82,17 @@ void scenario_pull() // all but length THEN("Push to content{start,valueType} works") { - const struct { + struct : public BlockData { struct { int start = 22; std::string valueType = "23"; } content; + const int &start() const { return content.start; } + int &start() { return content.start; } + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(24).start(25).valueType("26"); b.pullFromDerived(derived); CHECK(b.length() == 24); @@ -92,13 +102,17 @@ void scenario_pull() // all but start THEN("Push to content{length,valueType} works") { - const struct { + struct : public BlockData { struct { int length = 27; std::string valueType = "28"; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(29).start(30).valueType("31"); b.pullFromDerived(derived); CHECK(b.length() == 27); @@ -108,13 +122,17 @@ void scenario_pull() // all but valueType THEN("Push to content{length,start} works") { - const struct { + struct : public BlockData { struct { int length = 32; int start = 33; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } + const int &start() const { return content.start; } + int &start() { return content.start; } } derived; - BodyText b; + BlockData &b = derived; b.length(34).start(35).valueType("36"); b.pullFromDerived(derived); CHECK(b.length() == 32); @@ -124,14 +142,20 @@ void scenario_pull() // all three THEN("Push to content{length,start,valueType} works") { - const struct { + struct : public BlockData { struct { int length = 37; int start = 38; std::string valueType = "39"; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } + const int &start() const { return content.start; } + int &start() { return content.start; } + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(40).start(41).valueType("42"); b.pullFromDerived(derived); CHECK(b.length() == 37); @@ -142,14 +166,14 @@ void scenario_pull() } -SCENARIO("BodyText pull from content") { - GIVEN("A BodyText object") { +SCENARIO("BlockData pull from content") { + GIVEN("A BlockData object") { scenario_pull(); } } -SCENARIO("BodyText pull from content") { - GIVEN("A BodyText object") { +SCENARIO("BlockData pull from content") { + GIVEN("A BlockData object") { scenario_pull(); } } @@ -161,135 +185,161 @@ SCENARIO("BodyText pull from content") { // ----------------------------------------------------------------------------- // Helper -template +template void scenario_push() { WHEN("pushToDerived() is called") { // none of length, start, valueType THEN("Push to content{} works") { - struct { + struct : public BlockData { struct { int ignored = 123456; // not length, start, or valueType } content; + const int &ignored() const { return content.ignored; } + int &ignored() { return content.ignored; } } derived; - BodyText b; + BlockData &b = derived; b.length(0).start(0).valueType("0"); b.pushToDerived(derived); // should do nothing here - CHECK(derived.content.ignored == 123456); + CHECK(derived.ignored() == 123456); } // length only THEN("Push to content{length} works") { - struct { + struct : public BlockData { struct { int length = 10; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } } derived; - BodyText b; + BlockData &b = derived; b.length(11).start(12).valueType("13"); b.pushToDerived(derived); - CHECK(derived.content.length == 11); + CHECK(derived.length() == 11); } // start only THEN("Push to content{start} works") { - struct { + struct : public BlockData { struct { int start = 14; } content; + const int &start() const { return content.start; } + int &start() { return content.start; } } derived; - BodyText b; + BlockData &b = derived; b.length(15).start(16).valueType("17"); b.pushToDerived(derived); - CHECK(derived.content.start == 16); + CHECK(derived.start() == 16); } // valueType only THEN("Push to content{valueType} works") { - struct { + struct : public BlockData { struct { std::string valueType = "18"; } content; + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(19).start(20).valueType("21"); b.pushToDerived(derived); - CHECK(derived.content.valueType == "21"); + CHECK(derived.valueType() == "21"); } // all but length THEN("Push to content{start,valueType} works") { - struct { + struct : public BlockData { struct { int start = 22; std::string valueType = "23"; } content; + const int &start() const { return content.start; } + int &start() { return content.start; } + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(24).start(25).valueType("26"); b.pushToDerived(derived); - CHECK(derived.content.start == 25); - CHECK(derived.content.valueType == "26"); + CHECK(derived.start() == 25); + CHECK(derived.valueType() == "26"); } // all but start THEN("Push to content{length,valueType} works") { - struct { + struct : public BlockData { struct { int length = 27; std::string valueType = "28"; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(29).start(30).valueType("31"); b.pushToDerived(derived); - CHECK(derived.content.length == 29); - CHECK(derived.content.valueType == "31"); + CHECK(derived.length() == 29); + CHECK(derived.valueType() == "31"); } // all but valueType THEN("Push to content{length,start} works") { - struct { + struct : public BlockData { struct { int length = 32; int start = 33; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } + const int &start() const { return content.start; } + int &start() { return content.start; } } derived; - BodyText b; + BlockData &b = derived; b.length(34).start(35).valueType("36"); b.pushToDerived(derived); - CHECK(derived.content.length == 34); - CHECK(derived.content.start == 35); + CHECK(derived.length() == 34); + CHECK(derived.start() == 35); } // all three THEN("Push to content{length,start,valueType} works") { - struct { + struct : public BlockData { struct { int length = 37; int start = 38; std::string valueType = "39"; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } + const int &start() const { return content.start; } + int &start() { return content.start; } + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } } derived; - BodyText b; + BlockData &b = derived; b.length(40).start(41).valueType("42"); b.pushToDerived(derived); - CHECK(derived.content.length == 40); - CHECK(derived.content.start == 41); - CHECK(derived.content.valueType == "42"); + CHECK(derived.length() == 40); + CHECK(derived.start() == 41); + CHECK(derived.valueType() == "42"); } } } -SCENARIO("BodyText push to content") { - GIVEN("A BodyText object") { +SCENARIO("BlockData push to content") { + GIVEN("A BlockData object") { scenario_push(); } } -SCENARIO("BodyText push to content") { - GIVEN("A BodyText object") { +SCENARIO("BlockData push to content") { + GIVEN("A BlockData object") { scenario_push(); } } diff --git a/src/GNDStk/BodyText/test/toNode.test.cpp b/src/GNDStk/BlockData/test/toNode.test.cpp similarity index 52% rename from src/GNDStk/BodyText/test/toNode.test.cpp rename to src/GNDStk/BlockData/test/toNode.test.cpp index c54fb43ad..1fcd71b29 100644 --- a/src/GNDStk/BodyText/test/toNode.test.cpp +++ b/src/GNDStk/BlockData/test/toNode.test.cpp @@ -2,7 +2,7 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- @@ -11,67 +11,68 @@ using namespace njoy::GNDStk::core; // Tests use either void for each template parameter, or int, double, // std::string, and char, in that order. In the former case, we're testing -// toNode() for the generic BodyText<...,void>. In the latter case, we're -// testing toNode() for non-generic BodyText. +// toNode() for the generic BlockData<...,void>. In the latter case, we're +// testing toNode() for non-generic BlockData. template void scenario_toNode() { - // Default-constructed BodyText - GIVEN("A default-constructed BodyText") { + // Default-constructed BlockData + GIVEN("A default-constructed BlockData") { WHEN("toNode() is called") { THEN("The computed text string is empty") { - const BodyText b; - std::string text = "abc"; - struct { + struct : public BlockData { struct { } content; } derived; + BlockData &b = derived; + // w/trim == true (shouldn't matter here; applies to vector) b.trim = true; - b.toNode(text,derived); + b.toNode(text); CHECK(text == ""); // w/trim == false (shouldn't matter here; applies to vector) b.trim = false; - b.toNode(text,derived); + b.toNode(text); CHECK(text == ""); } } } - // BodyText, after being given a particular raw string and parameters - GIVEN("A BodyText, with given raw string and parameters") { + // BlockData, after being given a particular raw string and parameters + GIVEN("A BlockData, with given raw string and parameters") { WHEN("toNode() is called") { THEN("The computed text string is as expected, " "and the parameters remain as given" ) { - BodyText b; - b.string("0 12 34 56 0 0") - .start(100).length(200).valueType("hello"); - - std::string text = "abc"; - struct { + struct : public BlockData { struct { } content; } derived; + BlockData &b = derived; + + b.string("0 12 34 56 0 0") + .start(100).length(200).valueType("hello"); // Someone who sets the raw string directly (as opposed to using // a vector) is stating that they want *exactly* that raw string, - // including any 0s or anything else. To have BodyText's trim flag + // including any 0s or anything else. To have BlockData's trim flag // be relevant, use a vector. // w/trim == true (shouldn't matter here; applies to vector) + std::string text = "abc"; b.trim = true; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "0 12 34 56 0 0"); CHECK(b.start() == 100); CHECK(b.length() == 200); CHECK(b.valueType() == "hello"); // w/trim == false (shouldn't matter here; applies to vector) + text = "abc"; b.trim = false; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "0 12 34 56 0 0"); CHECK(b.start() == 100); CHECK(b.length() == 200); @@ -80,36 +81,37 @@ void scenario_toNode() } } - // BodyText, after being given a particular vector - GIVEN("A BodyText, assigned from a particular vector") { + // BlockData, after being given a particular vector + GIVEN("A BlockData, assigned from a particular vector") { WHEN("toNode() is called") { THEN("The computed text string is as expected, " "and the parameters were computed properly" ) { - BodyText b; + struct : public BlockData { + struct { + } content; + } derived; + BlockData &b = derived; + // what's set here should be replaced upon assignment from vector b.string("a b c").start(10).length(20).valueType("foobar"); // assign from vector b = std::vector{{0, 0, 12, 34, 56, 78, 0, 0, 0, 0, 0}}; - std::string text = "this should be replaced"; - struct { - struct { - } content; - } derived; - // w/trim == true + std::string text = "this should be replaced"; b.trim = true; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "12 34 56 78"); CHECK(b.start() == 2); CHECK(b.length() == 11); CHECK(b.valueType() == "Integer32"); // w/trim == false + text = "this should be replaced"; b.trim = false; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "0 0 12 34 56 78 0 0 0 0 0"); CHECK(b.start() == 0); CHECK(b.length() == 11); @@ -118,36 +120,37 @@ void scenario_toNode() } } - // BodyText, after being given a particular vector - GIVEN("A BodyText, assigned from a particular vector") { + // BlockData, after being given a particular vector + GIVEN("A BlockData, assigned from a particular vector") { WHEN("toNode() is called") { THEN("The computed text string is as expected, " "and the parameters were computed properly" ) { - BodyText b; + struct : public BlockData { + struct { + } content; + } derived; + BlockData &b = derived; + // what's set here should be replaced upon assignment from vector b.string("d e f").start(100).length(200).valueType("foobar"); // assign from vector b = std::vector{{0, 0, 0, 1.234, 5.678, 0, 0 }}; - std::string text = "this should be replaced"; - struct { - struct { - } content; - } derived; - // w/trim == true + std::string text = "this should be replaced"; b.trim = true; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "1.234 5.678"); CHECK(b.start() == 3); CHECK(b.length() == 7); CHECK(b.valueType() == "Float64"); // w/trim == false + text = "this should be replaced"; b.trim = false; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "0 0 0 1.234 5.678 0 0"); CHECK(b.start() == 0); CHECK(b.length() == 7); @@ -156,30 +159,30 @@ void scenario_toNode() } } - // BodyText, after being given a particular vector + // BlockData, after being given a particular vector // A key point here is to ensure that ""s (string "zeros") are properly // trimmed at the beginning and end. - GIVEN("A BodyText, assigned from a particular vector") { + GIVEN("A BlockData, assigned from a particular vector") { WHEN("toNode() is called") { THEN("The computed text string is as expected, " "and the parameters were computed properly" ) { - BodyText b; + struct : public BlockData { + struct { + } content; + } derived; + BlockData &b = derived; + // what's set here should be replaced upon assignment from vector b.string("d e f").start(100).length(200).valueType("foobar"); // assign from vector b = std::vector{{"","","","foo","bar","baz","",""}}; - std::string text = "this should be replaced"; - struct { - struct { - } content; - } derived; - // w/trim == true + std::string text = "this should be replaced"; b.trim = true; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "foo bar baz"); CHECK(b.start() == 3); CHECK(b.length() == 8); @@ -190,8 +193,9 @@ void scenario_toNode() // were true. If it didn't, we'd end up with, well, leading and // trailing *empty* strings, which, well, amount to leading and // trailing nothing. + text = "this should be replaced"; b.trim = false; - b.toNode(text,derived); + b.toNode(text); CHECK(text == "foo bar baz"); CHECK(b.start() == 3); CHECK(b.length() == 8); @@ -200,14 +204,19 @@ void scenario_toNode() } } - // BodyText, after being given a particular vector + // BlockData, after being given a particular vector // For T something other than int, double, and std::string - GIVEN("A BodyText, assigned from a particular vector") { + GIVEN("A BlockData, assigned from a particular vector") { WHEN("toNode() is called") { THEN("The computed text string is as expected, " "and the parameters were computed properly" ) { - BodyText b; + struct : public BlockData { + struct { + } content; + } derived; + BlockData &b = derived; + // what's set here should be replaced upon assignment from vector b.string("x y z").start(100).length(200).valueType("foobar"); @@ -215,12 +224,7 @@ void scenario_toNode() b = std::vector{{0,0,0,0,0,'a','b','c','d',0}}; std::string text = "this should be replaced"; - struct { - struct { - } content; - } derived; - - b.toNode(text,derived); + b.toNode(text); CHECK(text == "a b c d"); CHECK(b.start() == 5); CHECK(b.length() == 10); @@ -228,129 +232,6 @@ void scenario_toNode() } } } - - // Test various configurations of content{} when calling toNode(). Basically, - // these test whether toNode properly updates whichever of length, start, and - // valueType are in .content of toNode()'s second argument. - GIVEN("A BodyText, with vector") { - WHEN("toNode() is called") { - - // none of length, start, valueType - THEN("Push to content{} works") { - struct { - struct { - int ignored = 12345; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - // toNode doesn't care about what's *not* length/start/valueType... - CHECK(derived.content.ignored == 12345); // same as before toNode() - } - - // length only - THEN("Push to content{length} works") { - struct { - struct { - int length = 0; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - CHECK(derived.content.length == 8); - } - - // start only - THEN("Push to content{start} works") { - struct { - struct { - int start = 0; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - CHECK(derived.content.start == 2); - } - - // valueType only - THEN("Push to content{valueType} works") { - struct { - struct { - std::string valueType = ""; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - CHECK(derived.content.valueType == "Integer32"); - } - - // all but length - THEN("Push to content{start,valueType} works") { - struct { - struct { - int start = 0; - std::string valueType = ""; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - CHECK(derived.content.start == 2); - CHECK(derived.content.valueType == "Integer32"); - } - - // all but start - THEN("Push to content{length,valueType} works") { - struct { - struct { - int length = 0; - std::string valueType = ""; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - CHECK(derived.content.length == 8); - CHECK(derived.content.valueType == "Integer32"); - } - - // all but valueType - THEN("Push to content{length,start} works") { - struct { - struct { - int length = 0; - int start = 0; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - CHECK(derived.content.length == 8); - CHECK(derived.content.start == 2); - } - - // all three - THEN("Push to content{length,start,valueType} works") { - struct { - struct { - int length = 0; - int start = 0; - std::string valueType = ""; - } content; - } derived; - BodyText b; std::string text; - b = std::vector{{0,0,10,20,30,0,0,0}}; - b.toNode(text,derived); - CHECK(derived.content.length == 8); - CHECK(derived.content.start == 2); - CHECK(derived.content.valueType == "Integer32"); - } - } - } } @@ -358,10 +239,10 @@ void scenario_toNode() // Scenario // ----------------------------------------------------------------------------- -SCENARIO("BodyText toNode()") { +SCENARIO("BlockData toNode()") { scenario_toNode(); } -SCENARIO("BodyText toNode()") { +SCENARIO("BlockData toNode()") { scenario_toNode(); } diff --git a/src/GNDStk/BodyText/test/types.test.cpp b/src/GNDStk/BlockData/test/types.test.cpp similarity index 73% rename from src/GNDStk/BodyText/test/types.test.cpp rename to src/GNDStk/BlockData/test/types.test.cpp index 57274301f..debe6f3d6 100644 --- a/src/GNDStk/BodyText/test/types.test.cpp +++ b/src/GNDStk/BlockData/test/types.test.cpp @@ -2,115 +2,115 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- // Scenario // ----------------------------------------------------------------------------- -SCENARIO("BodyText data types") { +SCENARIO("BlockData data types") { GIVEN("std::vector for certain specific Ts") { - WHEN("Via BodyText, we make variants of vectors and of scalars") { + WHEN("Via BlockData, we make variants of vectors and of scalars") { // vector type - using vv = BodyText::VariantOfVectors; + using vv = BlockData::VariantOfVectors; THEN("The variant-of-vector size should be correct") { CHECK(std::variant_size_v == 15); } vv vectors; // scalar type - using vs = BodyText::VariantOfScalars; + using vs = BlockData::VariantOfScalars; THEN("The variant-of-scalar size should be correct") { CHECK(std::variant_size_v == 15); } vs scalars; - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,"a"); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == "a"); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,'b'); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == 'b'); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,-100); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == -100); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,-200); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == -200); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,-300); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == -300); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,-400); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == -400); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,-500); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == -500); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,100); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == 100); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,200); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == 200); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,300); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == 300); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,400); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == 400); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,500); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == 500); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,-1.2f); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == -1.2f); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,3.4); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == 3.4); } - THEN("Transformation to vector, and access, work") { + THEN("Transforming to vector, and access, work") { vectors = std::vector(10,-5.6); scalars = std::get>(vectors)[0]; CHECK(std::get(scalars) == -5.6); diff --git a/src/GNDStk/BlockData/test/write.test.cpp b/src/GNDStk/BlockData/test/write.test.cpp new file mode 100644 index 000000000..40c9ff182 --- /dev/null +++ b/src/GNDStk/BlockData/test/write.test.cpp @@ -0,0 +1,437 @@ + +#include "catch.hpp" +#include "GNDStk.hpp" + +using namespace njoy::GNDStk; + + +// ----------------------------------------------------------------------------- +// Scenario: raw string is active +// ----------------------------------------------------------------------------- + +// Helper: scenario_write_string_active +template +void scenario_write_string_active() +{ + // string is active, but empty + GIVEN("A BlockData with an empty raw string") { + WHEN("BlockData.write() is called") { + THEN("Nothing is printed") { + BlockData b; + b.string(""); + + // with no indentation + std::ostringstream oss; + b.write(oss,0); + CHECK(oss.str() == ""); + + // the same (nothing is printed) even if indentation is nonzero + oss.str(""); + b.write(oss,2); + CHECK(oss.str() == ""); + } + } + } + + // string is active, and contains values + GIVEN("A BlockData with a non-empty raw string") { + WHEN("BlockData.write() is called") { + THEN("The raw string and a newline are printed") { + BlockData b; + b.string("foo bar baz"); + + // with no indentation + std::ostringstream oss; + b.write(oss,0); + CHECK(oss.str() == "foo bar baz\n"); + + // the same, even if indentation is nonzero; indentation isn't + // applied when the raw string (as opposed to the ) is active + oss.str(""); + b.write(oss,2); + CHECK(oss.str() == "foo bar baz\n"); + } + } + } +} + +// For DATATYPE == void +SCENARIO("BlockData write(), when the raw string is active") +{ + scenario_write_string_active(); +} + +// For DATATYPE == double +SCENARIO("BlockData write(), when the raw string is active") +{ + scenario_write_string_active(); +} + + +// ----------------------------------------------------------------------------- +// Scenario: vector is active +// ----------------------------------------------------------------------------- + +SCENARIO("BlockData write(), when a vector is active") +{ + // vector is active, but empty + GIVEN("A BlockData with an empty vector") { + WHEN("BlockData.write() is called") { + THEN("Nothing is printed") { + BlockData b; + b = std::vector{}; + + // with no indentation + std::ostringstream oss; + b.write(oss,0); + CHECK(oss.str() == ""); + + // the same (nothing is printed) even if indentation is nonzero + oss.str(""); + b.write(oss,2); + CHECK(oss.str() == ""); + } + } + } + + // vector is active, and contains values + GIVEN("A BlockData with a non-empty vector") { + WHEN("BlockData.write() is called") { + THEN("The vector and a newline are printed") { + BlockData b; + b = std::vector{{2, 3, 5, 7, 11, 13, 17, 19, 21, 23}}; + std::ostringstream oss; + + // Cases: + // indent: 0, 3 (number of spaces per indentation level) + // columns: 0, 1, 2, 5, 10, 11 (note that 10 == vector size) + // level: 0, 1, 2 (indentation level) + // Note: columns <= 0 means unlimited: so, all values on one line. + // Lots of cases, but we want to check that our pretty-printing + // functionality works perfectly, and doesn't do anything that's + // unexpected around "boundaries" like columns==10 with 10 values. + + oss.str(""); indent = 0; columns = 0; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 0; b.write(oss,1); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 0; b.write(oss,2); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 1; b.write(oss,0); + CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); + + oss.str(""); indent = 0; columns = 1; b.write(oss,1); + CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); + + oss.str(""); indent = 0; columns = 1; b.write(oss,2); + CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); + + oss.str(""); indent = 0; columns = 2; b.write(oss,0); + CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); + + oss.str(""); indent = 0; columns = 2; b.write(oss,1); + CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); + + oss.str(""); indent = 0; columns = 2; b.write(oss,2); + CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); + + oss.str(""); indent = 0; columns = 5; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 5; b.write(oss,1); + CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 5; b.write(oss,2); + CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 10; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 10; b.write(oss,1); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 10; b.write(oss,2); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 11; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 11; b.write(oss,1); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 0; columns = 11; b.write(oss,2); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 0; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 0; b.write(oss,1); + CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 0; b.write(oss,2); + CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 1; b.write(oss,0); + CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); + + oss.str(""); indent = 3; columns = 1; b.write(oss,1); + CHECK(oss.str() == + " 2\n 3\n 5\n 7\n 11\n" + " 13\n 17\n 19\n 21\n 23\n"); + + oss.str(""); indent = 3; columns = 1; b.write(oss,2); + CHECK(oss.str() == + " 2\n 3\n 5\n 7\n 11\n" + " 13\n 17\n 19\n 21\n 23\n"); + + oss.str(""); indent = 3; columns = 2; b.write(oss,0); + CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); + + oss.str(""); indent = 3; columns = 2; b.write(oss,1); + CHECK( + oss.str() == + " 2 3\n 5 7\n 11 13\n 17 19\n 21 23\n" + ); + + oss.str(""); indent = 3; columns = 2; b.write(oss,2); + CHECK( + oss.str() == + " 2 3\n 5 7\n 11 13\n 17 19\n 21 23\n" + ); + + oss.str(""); indent = 3; columns = 5; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 5; b.write(oss,1); + CHECK(oss.str() == " 2 3 5 7 11\n 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 5; b.write(oss,2); + CHECK(oss.str() == " 2 3 5 7 11\n 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 10; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 10; b.write(oss,1); + CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 10; b.write(oss,2); + CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 11; b.write(oss,0); + CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 11; b.write(oss,1); + CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); + + oss.str(""); indent = 3; columns = 11; b.write(oss,2); + CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); + } + } + } +} + + + +// ----------------------------------------------------------------------------- +// Scenario: vector is active; test GNDStk::truncate +// ----------------------------------------------------------------------------- + +// Helper: test_truncate +template +void test_truncate( + const BlockData &b, + const int indent, + const std::size_t columns, + const int level, + const long truncate, + const std::string &want +) { + njoy::GNDStk::indent = indent; + njoy::GNDStk::columns = columns; + njoy::GNDStk::truncate = truncate; + + std::ostringstream oss; + b.write(oss,level); + + std::cout << "test_truncate:" << std::endl; + std::cout << oss.str() << std::endl; + + CHECK(oss.str() == want); +} + +SCENARIO("BlockData write(), vector, trying GNDStk::truncate") +{ + // vector is active, but empty + GIVEN("A BlockData with an empty vector") { + WHEN("BlockData.write() is called") { + THEN("Nothing is printed") { + BlockData b; + b = std::vector{}; + + using njoy::GNDStk::truncate; + for (truncate = -10; truncate <= 10; ++truncate) { + // with no indentation + std::ostringstream oss; + b.write(oss,0); + CHECK(oss.str() == ""); + + // the same (nothing is printed) even if indentation is nonzero + oss.str(""); + b.write(oss,2); + CHECK(oss.str() == ""); + } + } + } + } + + // vector is active, and contains values + GIVEN("A BlockData with a non-empty vector") { + WHEN("BlockData.write() is called") { + THEN("The vector and a newline are printed") { + BlockData b; + b = std::vector{{2.3, 5.7, 11.13, 17.19, 21.23}}; + + // Cases: + // indent: 3 (number of spaces per indentation level) + // columns: 0, 2, 3 (0 means unlimited) + // level: 0, 1, 2 (indentation level) + // truncate: -1, 0, 1, 2, 3 (-1 means none; print all values) + // Lots of cases; we want pretty-printing to be perfect. + + // Integral parameters below are: indent, columns, level, truncate + test_truncate(b, 3, 0, 0, -1, + "2.3 5.7 11.13 17.19 21.23\n"); + test_truncate(b, 3, 0, 0, +0, + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 0, +1, + "2.3\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 0, +2, + "2.3 5.7\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 0, +3, + "2.3 5.7 11.13\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 1, -1, + " 2.3 5.7 11.13 17.19 21.23\n"); + test_truncate(b, 3, 0, 1, +0, + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 1, +1, + " 2.3\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 1, +2, + " 2.3 5.7\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 1, +3, + " 2.3 5.7 11.13\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 2, -1, + " 2.3 5.7 11.13 17.19 21.23\n"); + test_truncate(b, 3, 0, 2, +0, + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 2, +1, + " 2.3\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 2, +2, + " 2.3 5.7\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 0, 2, +3, + " 2.3 5.7 11.13\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 0, -1, + "2.3 5.7\n" + "11.13 17.19\n" + "21.23\n"); + test_truncate(b, 3, 2, 0, +0, + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 0, +1, + "2.3\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 0, +2, + "2.3 5.7\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 0, +3, + "2.3 5.7\n" + "11.13\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 1, -1, + " 2.3 5.7\n" + " 11.13 17.19\n" + " 21.23\n"); + test_truncate(b, 3, 2, 1, +0, + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 1, +1, + " 2.3\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 1, +2, + " 2.3 5.7\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 1, +3, + " 2.3 5.7\n" + " 11.13\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 2, -1, + " 2.3 5.7\n" + " 11.13 17.19\n" + " 21.23\n"); + test_truncate(b, 3, 2, 2, +0, + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 2, +1, + " 2.3\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 2, +2, + " 2.3 5.7\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 2, 2, +3, + " 2.3 5.7\n" + " 11.13\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 0, -1, + "2.3 5.7 11.13\n" + "17.19 21.23\n"); + test_truncate(b, 3, 3, 0, +0, + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 0, +1, + "2.3\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 0, +2, + "2.3 5.7\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 0, +3, + "2.3 5.7 11.13\n" + "// truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 1, -1, + " 2.3 5.7 11.13\n" + " 17.19 21.23\n"); + test_truncate(b, 3, 3, 1, +0, + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 1, +1, + " 2.3\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 1, +2, + " 2.3 5.7\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 1, +3, + " 2.3 5.7 11.13\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 2, -1, + " 2.3 5.7 11.13\n" + " 17.19 21.23\n"); + test_truncate(b, 3, 3, 2, +0, + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 2, +1, + " 2.3\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 2, +2, + " 2.3 5.7\n" + " // truncated; total #values == 5\n"); + test_truncate(b, 3, 3, 2, +3, + " 2.3 5.7 11.13\n" + " // truncated; total #values == 5\n"); + } + } + } +} diff --git a/src/GNDStk/BodyText/src/fromNode.hpp b/src/GNDStk/BodyText/src/fromNode.hpp deleted file mode 100644 index c54b50027..000000000 --- a/src/GNDStk/BodyText/src/fromNode.hpp +++ /dev/null @@ -1,39 +0,0 @@ - -// ----------------------------------------------------------------------------- -// BodyText::fromNode(Node) -// ----------------------------------------------------------------------------- - -void fromNode(const Node &node) -{ - // length, start, and valueType might be present in the Node, but we won't - // fetch any of them here. Elsewhere, the current BodyText object should have - // its length, start, and valueType pulled from those respective values in - // an object of a class derived from Component (which in turn derives from - // BodyText). That object's content will have been pulled from the same Node. - // Here, we just get the Node's values: "plain character data" in XML terms. - - bool found = false; - rawstring = node.pcdata(found); - - if (!found) { - rawstring = ""; - - // Warning, re: why are we in BodyText if there's no body text? - // Perhaps it's possible that the Node has a non-default length and/or - // start, so that the values are all supposed to be...zero. Until and - // unless we discover otherwise, however, we doubt that that would be - // the case, and will consider a Node's lack of plain character data, - // in the present context, to be something that merits a warning. - log::warning( - "Component marked as having \"body text\", a.k.a. XML \"pcdata\" " - "(plain\ncharacter data), " - "but no such content was found in the GNDS node." - ); - log::member("BodyText::fromNode(Node, with name \"{}\")", node.name); - } - - // Above, we set the raw string. The following reflects this, so that the - // vector, or a vector in the variant, will be rebuilt from the raw string - // if and when a caller asks for it. - act = Active::string; -} diff --git a/src/GNDStk/BodyText/src/sync.hpp b/src/GNDStk/BodyText/src/sync.hpp deleted file mode 100644 index 98fa02830..000000000 --- a/src/GNDStk/BodyText/src/sync.hpp +++ /dev/null @@ -1,28 +0,0 @@ - -// pullFromDerived(derived) -// Make this BodyText's length, start, and valueType be consistent with any or -// all such parameters that exist in the given object. Remember that this class, -// BodyText, is a base of Component, which is a base of some other class. -template -void pullFromDerived(const T &obj) -{ - if constexpr (detail::hasLength) - length(obj.content.length); - if constexpr (detail::hasStart) - start(obj.content.start); - if constexpr (detail::hasValueType) - valueType(obj.content.valueType); -} - -// pushToDerived(derived) -// The reverse of the above. -template -void pushToDerived(T &obj) const -{ - if constexpr (detail::hasLength) - obj.content.length = length(); - if constexpr (detail::hasStart) - obj.content.start = start(); - if constexpr (detail::hasValueType) - obj.content.valueType = valueType(); -} diff --git a/src/GNDStk/BodyText/src/write.hpp b/src/GNDStk/BodyText/src/write.hpp deleted file mode 100644 index 98dbb4a64..000000000 --- a/src/GNDStk/BodyText/src/write.hpp +++ /dev/null @@ -1,72 +0,0 @@ - -// ----------------------------------------------------------------------------- -// write -// To an ostream (not to a Node; that a different thing) -// ----------------------------------------------------------------------------- - -std::ostream &write(std::ostream &os, const int level) const -{ - // If empty, don't even write a newline - if ((active() == Active::string && rawstring == "") || - (active() == Active::vector && size() == 0)) - return os; - - // ------------------------ - // If string is active - // ------------------------ - - if (active() == Active::string) { - // write the string exactly as-is, without our column formatting - // or any indentation; then also write a newline - GNDStk::color && GNDStk::colors::value != "" - ? os << colors::value << rawstring << colors::reset - : os << rawstring; - return os << std::endl; - } - - // ------------------------ - // If vector is active - // ------------------------ - - // Indentation (string, with some number of spaces) - const auto indent = std::string(GNDStk::indent*level,' '); - - const auto writeLambda = - [&os,&indent](auto &&alt) - { - std::size_t count = 0; - using T = std::decay_t; - - // use our column formatting - for (auto &element : alt) { - count == 0 - ? os << indent - : GNDStk::across == 0 || count % GNDStk::across != 0 - ? os << ' ' - : os << '\n' << indent; - - if (GNDStk::color && GNDStk::colors::value != "") - os << colors::value; - - if constexpr (std::is_floating_point_v) - os << detail::Precision< - detail::PrecisionContext::data, - T - >{}.write(element); - else - os << element; - - if (GNDStk::color && GNDStk::colors::value != "") - os << colors::reset; - - count++; - }; - }; - - if constexpr (runtime) - std::visit(writeLambda,variant); - else - writeLambda(vector); - - return os << std::endl; -} diff --git a/src/GNDStk/BodyText/test/write.test.cpp b/src/GNDStk/BodyText/test/write.test.cpp deleted file mode 100644 index ac0095b27..000000000 --- a/src/GNDStk/BodyText/test/write.test.cpp +++ /dev/null @@ -1,226 +0,0 @@ - -#include "catch.hpp" -#include "GNDStk.hpp" - -using namespace njoy::GNDStk::core; - - -// ----------------------------------------------------------------------------- -// Scenario: raw string -// ----------------------------------------------------------------------------- - -// Helper -template -void scenario_write_string_active() -{ - GIVEN("A BodyText with an empty raw string") { - WHEN("BodyText.write() is called") { - THEN("Nothing is printed") { - BodyText b; - b = std::vector{{'a','b','c'}}; - b.string(""); // should make string (not vector) active - std::ostringstream oss; - b.write(oss,0); - CHECK(oss.str() == ""); - - // same (nothing printed) if indentation is non-0 - b.write(oss,2); - CHECK(oss.str() == ""); - } - } - } - - GIVEN("A BodyText with a non-empty raw string") { - WHEN("BodyText.write() is called") { - THEN("The raw string and a newline are printed") { - BodyText b; - b = std::vector{{'a','b','c'}}; - b.string("foo bar"); // should make string (not vector) active - - std::ostringstream oss; - b.write(oss,0); - CHECK(oss.str() == "foo bar\n"); - - // indentation isn't applied when the raw string is active - oss.str(""); - b.write(oss,2); - CHECK(oss.str() == "foo bar\n"); - } - } - } -} - -// For DATA == void -SCENARIO("BodyText write(), when the raw string is active") { - scenario_write_string_active(); -} - -// For DATA != void -SCENARIO("BodyText write(), when the raw string is active") { - scenario_write_string_active(); -} - - -// ----------------------------------------------------------------------------- -// Scenario: vector -// ----------------------------------------------------------------------------- - -// Helper -template -void scenario_write_vector_active() -{ - GIVEN("A BodyText with an empty vector") { - WHEN("BodyText.write() is called") { - THEN("Nothing is printed") { - BodyText b; - b.string("should be ignored"); // because vector is forthcoming... - b = std::vector{}; - std::ostringstream oss; - b.write(oss,0); - CHECK(oss.str() == ""); - - // same (nothing printed) if indentation is non-0 - b.write(oss,2); - CHECK(oss.str() == ""); - } - } - } - - GIVEN("A BodyText with a non-empty vector") { - WHEN("BodyText.write() is called") { - THEN("The vector and a newline are printed") { - BodyText b; - b.string("should be ignored"); // because vector is forthcoming... - b = std::vector{{2,3,5,7,11,13,17,19,21,23}}; - std::ostringstream oss; - - // Cases: - // indent: 0, 3 (number of spaces per indentation level) - // across: 0, 1, 2, 5, 10, 11 (note that 10 == vector size) - // level: 0, 1, 2 (indentation level) - // Note: across == 0 ==> unlimited. - // Lots of cases, but we want to check that our pretty-printing - // functionality works perfectly, and doesn't do anything that's - // unexpected around "boundaries" like across==10 with 10 values. - - oss.str(""); indent = 0; across = 0; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 0; b.write(oss,1); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 0; b.write(oss,2); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 1; b.write(oss,0); - CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); - - oss.str(""); indent = 0; across = 1; b.write(oss,1); - CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); - - oss.str(""); indent = 0; across = 1; b.write(oss,2); - CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); - - oss.str(""); indent = 0; across = 2; b.write(oss,0); - CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); - - oss.str(""); indent = 0; across = 2; b.write(oss,1); - CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); - - oss.str(""); indent = 0; across = 2; b.write(oss,2); - CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); - - oss.str(""); indent = 0; across = 5; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 5; b.write(oss,1); - CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 5; b.write(oss,2); - CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 10; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 10; b.write(oss,1); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 10; b.write(oss,2); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 11; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 11; b.write(oss,1); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 0; across = 11; b.write(oss,2); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 0; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 0; b.write(oss,1); - CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 0; b.write(oss,2); - CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 1; b.write(oss,0); - CHECK(oss.str() == "2\n3\n5\n7\n11\n13\n17\n19\n21\n23\n"); - - oss.str(""); indent = 3; across = 1; b.write(oss,1); - CHECK(oss.str() == " 2\n 3\n 5\n 7\n 11\n 13\n 17\n 19\n 21\n 23\n"); - - oss.str(""); indent = 3; across = 1; b.write(oss,2); - CHECK(oss.str() == " 2\n 3\n 5\n 7\n 11\n 13\n 17\n 19\n 21\n 23\n"); - - oss.str(""); indent = 3; across = 2; b.write(oss,0); - CHECK(oss.str() == "2 3\n5 7\n11 13\n17 19\n21 23\n"); - - oss.str(""); indent = 3; across = 2; b.write(oss,1); - CHECK(oss.str() == " 2 3\n 5 7\n 11 13\n 17 19\n 21 23\n"); - - oss.str(""); indent = 3; across = 2; b.write(oss,2); - CHECK(oss.str() == " 2 3\n 5 7\n 11 13\n 17 19\n 21 23\n"); - - oss.str(""); indent = 3; across = 5; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11\n13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 5; b.write(oss,1); - CHECK(oss.str() == " 2 3 5 7 11\n 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 5; b.write(oss,2); - CHECK(oss.str() == " 2 3 5 7 11\n 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 10; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 10; b.write(oss,1); - CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 10; b.write(oss,2); - CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 11; b.write(oss,0); - CHECK(oss.str() == "2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 11; b.write(oss,1); - CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); - - oss.str(""); indent = 3; across = 11; b.write(oss,2); - CHECK(oss.str() == " 2 3 5 7 11 13 17 19 21 23\n"); - } - } - } -} - -// For DATA == void -SCENARIO("BodyText write(), when a vector is active") { - scenario_write_vector_active(); -} - -// For DATA != void -SCENARIO("BodyText write(), when a vector is active") { - scenario_write_vector_active(); -} diff --git a/src/GNDStk/Child.hpp b/src/GNDStk/Child.hpp index 4cb0bc77c..019e8f3f3 100644 --- a/src/GNDStk/Child.hpp +++ b/src/GNDStk/Child.hpp @@ -35,7 +35,7 @@ ALLOW tree(...,axes,axis) gives back a container of axis objects, not a single axis object, because - our Child axis keyword has ALLOW == Allow::any. Note that axes, not to be + our Child axis object has ALLOW == Allow::any. Note that axes, not to be confused with axis, has ALLOW == Allow::one because it's expected just once. CONVERTER @@ -72,12 +72,6 @@ class Child { const TYPE object; CONVERTER converter; // optional custom converter; needs operator() FILTER filter; // optional custom filter; needs operator() -private: - // allowable as top-level? - // We make this private, with a setter and a getter, so that the setter - // can register the name as "allowable as top-level" if we set to true. - bool canBeTopLevel; -public: // ------------------------ // constructors @@ -87,36 +81,14 @@ class Child { // name, type // name, type, converter // name, type, converter, filter - // name, type, converter, filter, top explicit Child( const std::string &n, const TYPE &t = TYPE{}, const CONVERTER &c = CONVERTER{}, - const FILTER &f = FILTER{}, - const bool canbetop = false + const FILTER &f = FILTER{} ) : name(n), object(t), converter(c), filter(f) - { - top(canbetop); - } - - // ------------------------ - // top(): allowable - // as a top-level node? - // ------------------------ - - // top() - bool top() const - { - return canBeTopLevel; - } - - // top(bool) - void top(const bool t) - { - if (t) detail::AllowedTop.insert(name); - canBeTopLevel = t; - } + { } // ------------------------ // simple functions @@ -128,7 +100,7 @@ class Child { auto basic() const { return Child( - name, filter, canBeTopLevel // converter not possible here + name, filter // converter not possible here ); } @@ -137,7 +109,7 @@ class Child { auto one() const { return Child( - name, object, converter, filter, canBeTopLevel + name, object, converter, filter ); } @@ -146,7 +118,7 @@ class Child { auto many() const { return Child( - name, object, converter, filter, canBeTopLevel + name, object, converter, filter ); } }; @@ -173,10 +145,6 @@ class Child { // name, filter std::string name; FILTER filter; // optional custom filter; needs operator() -private: - // allowable as top-level? - bool canBeTopLevel; -public: // ------------------------ // constructors @@ -184,34 +152,12 @@ class Child { // name // name, filter - // name, filter, top explicit Child( const std::string &n, - const FILTER &f = FILTER{}, - const bool t = false + const FILTER &f = FILTER{} ) : name(n), filter(f) - { - top(t); - } - - // ------------------------ - // top(): allowable - // as a top-level node? - // ------------------------ - - // top() - bool top() const - { - return canBeTopLevel; - } - - // top(bool) - void top(const bool t) - { - if (t) detail::AllowedTop.insert(name); - canBeTopLevel = t; - } + { } // ------------------------ // simple functions @@ -222,21 +168,21 @@ class Child { // we're already void; this is here for consistency with the general case. auto basic() const { - return Child(name,filter,canBeTopLevel); + return Child(name,filter); } // one() // Produce an equivalent Child, but formulated as Allow::one auto one() const { - return Child(name,filter,canBeTopLevel); + return Child(name,filter); } // many() // Produce an equivalent Child, but formulated as Allow::many auto many() const { - return Child(name,filter,canBeTopLevel); + return Child(name,filter); } }; @@ -246,13 +192,12 @@ class Child { // Macro // ----------------------------------------------------------------------------- -// For Child building. This macro doesn't handle the optional "top-level" -// flag, the converter, or the filter; we don't believe those will be needed -// very often. If you do need to provide one or both, construct a Child -// in some other way than by using this macro. +// For Child building. This macro doesn't handle the converter or the filter; +// we don't believe those will be needed very often. If you do need to provide +// one or both, construct a Child in some other way than by using this macro. #define GNDSTK_MAKE_CHILD(TYPE,name,ALLOW) \ - inline const Child name(#name) + inline const njoy::GNDStk::Child name(#name) // Note: we don't #undef this after we use it within GNDStk, as we might // normally do, because users might find it handy. diff --git a/src/GNDStk/Child/src/operator.hpp b/src/GNDStk/Child/src/operator.hpp index 0bbe2c677..b6df6bf06 100644 --- a/src/GNDStk/Child/src/operator.hpp +++ b/src/GNDStk/Child/src/operator.hpp @@ -16,42 +16,6 @@ inline auto operator-(const Child &kwd) -// ----------------------------------------------------------------------------- -// operator~: Allow as top-level -// operator!: Disallow as top-level -// -// We don't expect much use of these, but someone may occasionally find the -// first (more likely) or the second (less likely) to be helpful. The unary -// operators that we chose for these purposes seemed like the best, given a -// limited selection. One could think: (T)ilde for (T)op; Not for Not. -// ----------------------------------------------------------------------------- - -// operator~ -template -inline auto operator~(const Child &kwd) -{ - auto ret = kwd; - ret.top(true); - return ret; -} - -// operator! -// fixme This switches off the canBeTopLevel flag in an individual Child, -// but we actually use the namespace-scope set AllowedTop when we -// determine if a particular name is allowed as a top-level node. So, turning -// of a Child's previously-true top-level designator does not, at the moment, -// have any meaningful effect anywhere. We'll look at this more carefully -// sometime. For now, this just isn't a super important issue. -template -inline auto operator!(const Child &kwd) -{ - auto ret = kwd; - ret.top(false); - return ret; -} - - - // ----------------------------------------------------------------------------- // T/Child // Change type to T @@ -68,8 +32,7 @@ inline auto operator/( kwd.name, object, kwd.converter, - kwd.filter, - kwd.top() + kwd.filter ); } @@ -84,8 +47,7 @@ inline auto operator/( kwd.name, object, CONVERTER{}, // because the input Child didn't have one - kwd.filter, - kwd.top() + kwd.filter ); } @@ -174,8 +136,7 @@ inline Child< kwd.name, kwd.object, converter, // the new one; not kwd.converter! - kwd.filter, - kwd.top() + kwd.filter ); } @@ -215,8 +176,7 @@ inline auto operator--( kwd.name, kwd.object, C{}, - kwd.filter, - kwd.top() + kwd.filter ); } @@ -267,8 +227,7 @@ inline auto operator+( kwd.name, kwd.object, kwd.converter, - filter, // the new one - kwd.top() + filter // the new one ); } @@ -280,8 +239,7 @@ inline auto operator+( ) { return Child( kwd.name, - filter, // the new one - kwd.top() + filter // the new one ); } @@ -305,9 +263,7 @@ inline auto operator||( // both names, space-separated; this gets special treatment elsewhere a.name + " " + b.name, // need a filter; use the first Child's - a.filter, - // if either is top-level enabled - a.top() || b.top() + a.filter ); } @@ -329,8 +285,6 @@ inline auto operator||( // both names, space-separated; this gets special treatment elsewhere a.name + " " + b.name, // need an object, converter, and filter; use the first Child's - a.object, a.converter, a.filter, - // if either is top-level enabled - a.top() || b.top() + a.object, a.converter, a.filter ); } diff --git a/src/GNDStk/Child/test/Child.test.cpp b/src/GNDStk/Child/test/Child.test.cpp index ac43846b9..c0de1b37e 100644 --- a/src/GNDStk/Child/test/Child.test.cpp +++ b/src/GNDStk/Child/test/Child.test.cpp @@ -24,28 +24,24 @@ SCENARIO("Testing GNDStk Child") { const Child foo("foo"); CHECK(foo.name == "foo"); - CHECK(!foo.top()); } WHEN("Constructed with (name,converter)") { const Child foo("foo", 0.0, converter{}); CHECK(foo.name == "foo"); - CHECK(!foo.top()); } WHEN("Constructed with (name,converter,filter)") { const Child foo("foo", 0.0, converter{}, filter); CHECK(foo.name == "foo"); - CHECK(!foo.top()); } WHEN("Constructed with (name,converter,filter,top)") { const Child - foo("foo", 0.0, converter{}, filter, true); + foo("foo", 0.0, converter{}, filter); CHECK(foo.name == "foo"); - CHECK(foo.top()); } } @@ -55,21 +51,18 @@ SCENARIO("Testing GNDStk Child") { const Child foo("foo"); CHECK(foo.name == "foo"); - CHECK(!foo.top()); } WHEN("Constructed with (name,filter)") { const Child foo("foo", filter); CHECK(foo.name == "foo"); - CHECK(!foo.top()); } WHEN("Constructed with (name,filter,top)") { const Child - foo("foo", filter, true); + foo("foo", filter); CHECK(foo.name == "foo"); - CHECK(foo.top()); } } } diff --git a/src/GNDStk/Child/test/operator.test.cpp b/src/GNDStk/Child/test/operator.test.cpp index 1ec2af774..af7646875 100644 --- a/src/GNDStk/Child/test/operator.test.cpp +++ b/src/GNDStk/Child/test/operator.test.cpp @@ -128,45 +128,6 @@ SCENARIO("Testing GNDStk Child operators") { } } - // ------------------------ - // ~Child - // !Child - // top() - // top(bool) - // ------------------------ - - GIVEN("Some Child objects") { - const Child foo("foo"); - const Child bar("bar"); - Child one("one"); - one.top(true); - Child two("two"); - two.top(true); - - CHECK(!foo.top()); - CHECK(!bar.top()); - CHECK(one.top()); - CHECK(two.top()); - - WHEN("We apply ~Child") { - THEN("Top-level is enabled") { - CHECK((~foo).top()); - CHECK((~bar).top()); - CHECK((~one).top()); - CHECK((~two).top()); - } - } - - WHEN("We apply !Child") { - THEN("Top-level is disabled") { - CHECK(!(!foo).top()); - CHECK(!(!bar).top()); - CHECK(!(!one).top()); - CHECK(!(!two).top()); - } - } - } - // ------------------------ // type/Child // ------------------------ diff --git a/src/GNDStk/Component.hpp b/src/GNDStk/Component.hpp index 942acea86..f96d03d9e 100644 --- a/src/GNDStk/Component.hpp +++ b/src/GNDStk/Component.hpp @@ -13,13 +13,15 @@ using helpMap = std::map; // Component // ----------------------------------------------------------------------------- -template -class Component : public BodyText +template +class Component : public BlockData { // For convenience - using body = BodyText; - using typename body::VariantOfVectors; - using typename body::VariantOfScalars; + using BLOCKDATA = BlockData; + using typename BLOCKDATA::VariantOfVectors; + using typename BLOCKDATA::VariantOfScalars; + static inline constexpr bool hasFields = + !std::is_same_v>; // Links to fields in the object of the derived class. I can't find a way // to do this in a decltype(DERIVED::keys())-aware manner, because DERIVED @@ -35,12 +37,12 @@ class Component : public BodyText // Copy and move *assignments* have the right behavior, however. Component &operator=(const Component &other) { - body::operator=(other); + BLOCKDATA::operator=(other); return *this; } Component &operator=(Component &&other) { - body::operator=(std::move(other)); + BLOCKDATA::operator=(std::move(other)); return *this; } @@ -51,23 +53,30 @@ class Component : public BodyText // See comments in finish.hpp #include "GNDStk/Component/src/finish.hpp" - // Intermediaries between derived-class getters, and getter functions - // in detail::. These shorten the code in the derived classes. + // Helpers for derived-class getters/setters. + // These shorten the code in the derived classes. #include "GNDStk/Component/src/getter.hpp" + #include "GNDStk/Component/src/setter.hpp" // Fallback for documentation() if DERIVED doesn't have help static inline helpMap help; public: + #include "GNDStk/Component/src/read.hpp" + #include "GNDStk/Component/src/write.hpp" #include "GNDStk/Component/src/fromNode.hpp" #include "GNDStk/Component/src/sort.hpp" #include "GNDStk/Component/src/toNode.hpp" // conversion to Node - #include "GNDStk/Component/src/write.hpp" - // You can (but don't need to) override the following in DERIVED + // You can (but need not) override the following in DERIVED static std::string namespaceName() { return ""; } + // base + // Convenient access to the BlockData base class + BLOCKDATA &baseBlockData() { return *this; } + const BLOCKDATA &baseBlockData() const { return *this; } + // derived // Convenient access to the derived class DERIVED &derived() @@ -80,15 +89,15 @@ class Component : public BodyText { try { return DERIVED::help.at(subject); - } - catch ( ... ) { + } catch ( ... ) { return "No help information is available"; } } - // Component << std::string - // Meaning: read the string's content (currently XML or JSON) into an object - // of the Component's DERIVED class. Uses Node's << std::string capability. + // Component << string + // Meaning: read the string's content (currently XML, JSON, or HDF5) into + // an object of the Component's DERIVED class. Uses Node's << string, which + // does most of the work. void operator<<(const std::string &str) { try { @@ -108,10 +117,10 @@ class Component : public BodyText // ostream << Component // ----------------------------------------------------------------------------- -template +template std::ostream &operator<<( std::ostream &os, - const Component &obj + const Component &obj ) { - return obj.write(os); + return obj.write(os,0); } diff --git a/src/GNDStk/Component/src/ctor.hpp b/src/GNDStk/Component/src/ctor.hpp index f289a5058..560b8c959 100644 --- a/src/GNDStk/Component/src/ctor.hpp +++ b/src/GNDStk/Component/src/ctor.hpp @@ -8,7 +8,7 @@ friend DERIVED; // ctor: fields template -Component(const body &other, ARGS &...args) : body(other) +Component(const BLOCKDATA &other, ARGS &...args) : BLOCKDATA(other) { // static_assert needs string literal #define pairing_error \ @@ -22,7 +22,7 @@ Component(const body &other, ARGS &...args) : body(other) // The parameters that are sent to this constructor must EXACTLY reflect // what we'd get from a DERIVED::keys() multi-query. - if constexpr (std::is_same_v>) { + if constexpr (!hasFields) { // keys is "empty" (std::tuple<>); that's OK, as long as ARGS is too static_assert( std::is_same_v, std::tuple<>>, diff --git a/src/GNDStk/Component/src/detail.hpp b/src/GNDStk/Component/src/detail.hpp index 05cf4322d..4c20ef7a1 100644 --- a/src/GNDStk/Component/src/detail.hpp +++ b/src/GNDStk/Component/src/detail.hpp @@ -1,37 +1,10 @@ // Forward declaration, needed by some things later -template +template class Component; - namespace detail { -// ----------------------------------------------------------------------------- -// colorize_*(text) -// ----------------------------------------------------------------------------- - -#define gndstkPaste(one,two) one ## two -#define gndstkColorFun(part) \ - inline std::string gndstkPaste(colorize_,part)(const std::string &text) \ - { \ - return GNDStk::color && colors::part != "" \ - ? colors::part + text + colors::reset \ - : text; \ - } - - // colorize_label() etc. - gndstkColorFun(label) - gndstkColorFun(colon) - gndstkColorFun(component) - gndstkColorFun(brace) - gndstkColorFun(bracket) - gndstkColorFun(comment) - -#undef gndstkColorFun -#undef gndstkPaste - - - // ----------------------------------------------------------------------------- // Functions: miscellaneous // ----------------------------------------------------------------------------- @@ -263,6 +236,9 @@ bool writeComponentPart( const std::string &color ) { if constexpr (is_base_of_Component::value) { + // Suppress "unused parameter" warnings + (void)value; (void)maxlen; + (void)label; (void)color; // T is derived from Component, and thus inherits a write() value.write(os,level); } else { @@ -407,58 +383,83 @@ const T &getter( static const std::string context = "getter {}::{}.{}({}) on vector"; try { - // todo Make this more efficient, e.g. by assuming that the vector's - // elements are sorted by index, so that the wanted value is likely - // to be found at [index]. - - const T *selected = nullptr; - for (auto &v : vec) { - const T *ptr = nullptr; - - if constexpr (isVariant::value) { - // T == variant - std::visit( - [&v,&index,&ptr](auto &&alternative) - { - if constexpr (hasIndex) - if (alternative.index() == index) - ptr = &v; - }, - v + if constexpr (hasIndex) { + // hasIndex + // T (or at least one alternative in T, if T is a variant) has a + // metadatum called "index". In this case, this function's size_t + // index parameter is interpreted to mean: find the object with + // an "index" metadatum that matches the parameter. Importantly, + // then, index in this case is ***NOT*** a C++ [index] index! + + // fixme Make the following more efficient, e.g. by assuming that the + // vector's elements are sorted by index, so that the wanted value is + // likely to be found at [index], even though (as stated above) [index] + // is not the interpretation here... + const T *selected = nullptr; + + for (auto &v : vec) { + const T *ptr = nullptr; + + if constexpr (isVariant::value) { + // T == variant + std::visit( + [&v,&index,&ptr](auto &&alternative) + { + if constexpr (hasIndex) + if (alternative.index() == index) + ptr = &v; + }, + v + ); + } else { + // T != variant + if constexpr (hasIndex) + if (v.index() == index) + ptr = &v; + } + + if (!ptr) + continue; + + if (selected) { + log::warning( + "Element with metadatum \"index\" {} was already found " + "in the vector.\n" + "Keeping the first element that was found.", + index + ); + log::member(context, nsname, clname, field, index); + } else + selected = ptr; + } // for + + if (!selected) { + log::error( + "Element with metadatum \"index\" {} was not found " + "in the vector" + std::string(vec.size() + ? "." + : ";\nin fact the vector is empty."), + index ); - } else { - // T != variant - if constexpr (hasIndex) - if (v.index() == index) - ptr = &v; + throw std::exception{}; } + return *selected; - if (!ptr) - continue; - - if (selected) { - log::warning( - "Element with index {} was already found in the vector.\n" - "Keeping the first element that was found.", - index - ); - log::member(context, nsname, clname, field, index); - } else - selected = ptr; - } // for + } else { - if (!selected) { - log::error( - "Element with index {} was not found in the vector" + - std::string(vec.size() ? "." : ";\nin fact the vector is empty."), - index - ); - throw std::exception{}; + // !hasIndex + // No "index" is anywhere to be found in T. Here, then, we interpret + // this function's index parameter to be a regular, C++ [index] index. + if (!(index < vec.size())) { + log::error( + "Index {} is out of range; vector size is {}.", + vec.size()); + throw std::exception{}; + } + return vec[index]; } - return *selected; - } catch (...) { // context // Example: prints "getter containers::Axes.axis(100)" @@ -731,46 +732,46 @@ bool compareRegular(const A &a, const B &b) // index? std::size_t aindex = 0; bool ahasindex = false; if constexpr (hasIndex) { - if constexpr (isOptional) { - if ((ahasindex = a.content.index.has_value())) - aindex = a.content.index.value(); + if constexpr (isOptional) { + if ((ahasindex = a.index().has_value())) + aindex = a.index().value(); } else { ahasindex = true; - aindex = a.content.index; + aindex = a.index(); } } std::size_t bindex = 0; bool bhasindex = false; if constexpr (hasIndex) { - if constexpr (isOptional) { - if ((bhasindex = b.content.index.has_value())) - bindex = b.content.index.value(); + if constexpr (isOptional) { + if ((bhasindex = b.index().has_value())) + bindex = b.index().value(); } else { bhasindex = true; - bindex = b.content.index; + bindex = b.index(); } } // label? std::string alabel = ""; bool ahaslabel = false; if constexpr (hasLabel) { - if constexpr (isOptional) { - if ((ahaslabel = a.content.label.has_value())) - alabel = a.content.label.value(); + if constexpr (isOptional) { + if ((ahaslabel = a.label().has_value())) + alabel = a.label().value(); } else { ahaslabel = true; - alabel = a.content.label; + alabel = a.label(); } } std::string blabel = ""; bool bhaslabel = false; if constexpr (hasLabel) { - if constexpr (isOptional) { - if ((bhaslabel = b.content.label.has_value())) - blabel = b.content.label.value(); + if constexpr (isOptional) { + if ((bhaslabel = b.label().has_value())) + blabel = b.label().value(); } else { bhaslabel = true; - blabel = b.content.label; + blabel = b.label(); } } diff --git a/src/GNDStk/Component/src/finish.hpp b/src/GNDStk/Component/src/finish.hpp index 864f5d5bf..c6dd4eae4 100644 --- a/src/GNDStk/Component/src/finish.hpp +++ b/src/GNDStk/Component/src/finish.hpp @@ -48,11 +48,11 @@ Specifically, the constructors in the auto-generated classes call: Constructor from a Node: Component::finish(the Node) - Constructors involving a vector of "body text" data: + Constructors involving a vector of block data: Component::finish(the vector) Note: In the last case, we're speaking of a vector that's specifically for -body text, not a vector that might be there for a different reason. +block data, not a vector that might be there for a different reason. */ @@ -73,7 +73,7 @@ void construct(const Node &) { } // return void. We use bool, here, for technical reasons, relating to the test // used in the template finish() function (as opposed to the non-template cases) // to determine whether or not someone has provided a custom construct(). -template>> +template>> bool construct(const std::vector &) { return true; } @@ -88,10 +88,10 @@ bool construct(const std::vector &) { return true; } void finish() { - // If hasBodyText == true (else no-op), have Component's BodyText base + // If hasBlockData == true (else no-op), have Component's BlockData base // get length, start, and valueType, as available, from the derived class - if constexpr (hasBodyText) - body::pullFromDerived(derived()); + if constexpr (hasBlockData) + BLOCKDATA::pullFromDerived(derived()); // Based on the derived class' keys(), locate and sort derived-class fields // that are vectors, with vector elements that have index and/or label. @@ -109,8 +109,8 @@ void finish() void finish(const DERIVED &other) { // length, start, valueType - if constexpr (hasBodyText) - body::pullFromDerived(derived()); + if constexpr (hasBlockData) + BLOCKDATA::pullFromDerived(derived()); // derived-class vector fields sort(); @@ -132,15 +132,15 @@ void finish(const DERIVED &other) void finish(const Node &node) { // Read fields from the Node into the derived object. This applies the keys() - // multi-query in the derived class, and also runs BodyText::fromNode() if - // the Node has body text, in order to get the Node's string of "body text". + // multi-query in the derived class, and also runs BlockData::fromNode() if + // the Node has block data, in order to get the Node's string of block data. fromNode(node); - if constexpr (hasBodyText) { + if constexpr (hasBlockData) { // length, start, valueType - body::pullFromDerived(derived()); + BLOCKDATA::pullFromDerived(derived()); // make vector - body::get(); + BLOCKDATA::get(); } // derived-class vector fields @@ -160,15 +160,15 @@ void finish(const Node &node) // finish(vector) // ------------------------ -template>> +template>> void finish(const std::vector &vector) { // assign from the vector - body::operator=(vector); + BLOCKDATA::operator=(vector); // length, start, valueType: push back up to derived, // as they would have been computed above in operator=. - body::pushToDerived(derived()); + BLOCKDATA::pushToDerived(derived()); // derived-class vector fields sort(); diff --git a/src/GNDStk/Component/src/fromNode.hpp b/src/GNDStk/Component/src/fromNode.hpp index f7760b4ca..23f98fe4c 100644 --- a/src/GNDStk/Component/src/fromNode.hpp +++ b/src/GNDStk/Component/src/fromNode.hpp @@ -30,7 +30,7 @@ void fromNode(const Node &node) throw std::exception{}; } - if constexpr (std::is_same_v>) { + if constexpr (!hasFields) { // consistency check; then nothing further to do assert(0 == links.size()); } else { @@ -53,9 +53,9 @@ void fromNode(const Node &node) ); } - // body text, a.k.a. XML "pcdata" (plain character data), if any - if constexpr (hasBodyText) - body::fromNode(node); + // block data, a.k.a. XML "pcdata" (plain character data), if any + if constexpr (hasBlockData) + BLOCKDATA::fromNode(node); } catch (...) { log::member("Component.fromNode(Node(\"{}\"))", node.name); diff --git a/src/GNDStk/Component/src/read.hpp b/src/GNDStk/Component/src/read.hpp new file mode 100644 index 000000000..af5556ff5 --- /dev/null +++ b/src/GNDStk/Component/src/read.hpp @@ -0,0 +1,54 @@ + +// ----------------------------------------------------------------------------- +// Component::read() +// Via Node, and using Node's available read() functions. +// So, autogenerated classes can directly use .read(...). +// ----------------------------------------------------------------------------- + +// read(istream, FileType) +std::istream &read( + std::istream &is, + FileType format = FileType::null, + const bool decl = false +) { + Node node; + std::istream &ret = node.read(is, format, decl); + derived() = DERIVED(node); + return ret; +} + +// read(file name, FileType) +bool read( + const std::string &filename, + const FileType format = FileType::null, + const bool decl = false +) { + Node node; + bool ret = node.read(filename, format, decl); + derived() = DERIVED(node); + return ret; +} + +// read(istream, string) +std::istream &read( + std::istream &is, + const std::string &format, + const bool decl = false +) { + Node node; + std::istream &ret = node.read(is, format, decl); + derived() = DERIVED(node); + return ret; +} + +// read(file name, string) +bool read( + const std::string &filename, + const std::string &format, + const bool decl = false +) { + Node node; + bool ret = node.read(filename, format, decl); + derived() = DERIVED(node); + return ret; +} diff --git a/src/GNDStk/Component/src/setter.hpp b/src/GNDStk/Component/src/setter.hpp new file mode 100644 index 000000000..58b4a1e96 --- /dev/null +++ b/src/GNDStk/Component/src/setter.hpp @@ -0,0 +1,29 @@ + +// Like getter.hpp, but to help with *setters* in Component-derived classes. + +// push_back a value into the vector. +template< + class T, class FROM, + class = std::enable_if_t< + std::is_constructible_v || std::is_convertible_v + > +> +void setter(std::vector &vec, const FROM &value) +{ + vec.push_back(value); +} + +// Create an empty vector in the optional if it has no value, then +// push_back a value into the vector. +template< + class T, class FROM, + class = std::enable_if_t< + std::is_constructible_v || std::is_convertible_v + > +> +void setter(std::optional> &opt, const FROM &value) +{ + if (!opt.has_value()) + opt = std::vector{}; + opt->push_back(value); +} diff --git a/src/GNDStk/Component/src/sort.hpp b/src/GNDStk/Component/src/sort.hpp index 68dd7b309..b21f0ed1f 100644 --- a/src/GNDStk/Component/src/sort.hpp +++ b/src/GNDStk/Component/src/sort.hpp @@ -6,7 +6,7 @@ void sort() { try { - if constexpr (std::is_same_v>) { + if constexpr (!hasFields) { // Consistency check; then nothing further to do assert(0 == links.size()); } else { diff --git a/src/GNDStk/Component/src/toNode.hpp b/src/GNDStk/Component/src/toNode.hpp index 52c291d5b..b0707b0ed 100644 --- a/src/GNDStk/Component/src/toNode.hpp +++ b/src/GNDStk/Component/src/toNode.hpp @@ -4,40 +4,50 @@ // conversion to Node // ----------------------------------------------------------------------------- -// Normally we'd need just a const version of a conversion operator, and, if -// we needed a non-const version at all, it could build on the const version. -// A glitch in the present circumstances is that BodyText::toNode(), which is -// called from within these, splits const and non-const cases, and that needs -// to be preserved here. So, then, why does BodyText::toNode() have a non-const -// version? The issue is that in the non-const case, BodyText::toNode() may -// need to deal with a vector (not just an original "body text" string as may -// have been read into a const BodyText). And, dealing with a vector means -// computing a proper length, start, and valueType while doing toNode() - and -// pushing those up to the class derived from Component, as it's from that -// class that those fields are written to the Node. The need to compute proper -// values for those parameters is why we need the non-const case. (And we can't -// just make length etc. mutable in BodyText, as the length etc. in the derived -// class come into play too.) Maybe we'll work out a different way to handle -// all this, but for now, we have the following. - // const operator Node() const { + // Initialize a Node, with the necessary name + Node node(DERIVED::GNDSName()); + try { - #include "GNDStk/Component/src/toNodeBody.hpp" + // Handle block data, if applicable + if constexpr (hasBlockData) { + // GNDStk uses a "#text" metadatum of a "#pcdata" child node for this + std::string &text = node.add("#pcdata").add("#text","").second; + BLOCKDATA::toNode(text); + } + + // Write fields + if constexpr (!hasFields) { + // consistency check + assert(0 == links.size()); + } else { + // make tuple (of individual keys) from DERIVED::keys() + const auto tup = toKeywordTup(DERIVED::keys()).tup; + + // consistency check + assert(std::tuple_size::value == links.size()); + + // apply links: + // derived-class data ==> Node + // Below, each apply'd "key" is one value from DERIVED::keys(), and + // is a Meta, Child, or pair. The cast gives the + // underlying raw data type - int, say, or std::string - so that we + // can correctly use our generic void* link to a derived-class field. + std::apply( + [this,&node](const auto &... key) { + std::size_t n = 0; + (node.add(key,*(std::decay_t*)links[n++]), + ...); + }, + tup + ); + } } catch (...) { log::member("Component.operator Node() const"); throw; } -} -// non-const -operator Node() -{ - try { - #include "GNDStk/Component/src/toNodeBody.hpp" - } catch (...) { - log::member("Component.operator Node()"); - throw; - } + return node; } diff --git a/src/GNDStk/Component/src/toNodeBody.hpp b/src/GNDStk/Component/src/toNodeBody.hpp deleted file mode 100644 index a5182a737..000000000 --- a/src/GNDStk/Component/src/toNodeBody.hpp +++ /dev/null @@ -1,40 +0,0 @@ - -// 1. Initialize a Node, with the necessary name -Node node(DERIVED::GNDSName()); - -// 2. Body text, if applicable -if constexpr (hasBodyText) { - // GNDStk uses a "text" metadatum of a "pcdata" child node for this - std::string &text = node.add("pcdata").add("text","").second; - // Note: the following call might compute length, start, and valueType; - // so we need all of this before the upcoming writing of fields. - body::toNode(text,derived().content); -} - -// 3. Write fields -if constexpr (std::is_same_v>) { - // consistency check - assert(0 == links.size()); -} else { - // make tuple (of individual keys) from DERIVED::keys() - const auto tup = toKeywordTup(DERIVED::keys()).tup; - - // consistency check - assert(std::tuple_size::value == links.size()); - - // apply links: - // derived-class data ==> Node - // Below, each apply'd "key" is one value from DERIVED::keys(), and - // is a Meta, Child, or pair. The cast gives the - // underlying raw data type - int, say, or std::string - so that we - // can correctly use our generic void* link to a derived-class field. - std::apply( - [this,&node](const auto &... key) { - std::size_t n = 0; - (node.add(key,*(std::decay_t*)links[n++]), ...); - }, - tup - ); -} - -return node; diff --git a/src/GNDStk/Component/src/write.hpp b/src/GNDStk/Component/src/write.hpp index a2a8507bb..1b6b872e1 100644 --- a/src/GNDStk/Component/src/write.hpp +++ b/src/GNDStk/Component/src/write.hpp @@ -1,9 +1,9 @@ // ----------------------------------------------------------------------------- -// Component::write() +// Component::write(), for "prettyprinting" // ----------------------------------------------------------------------------- -std::ostream &write(std::ostream &os = std::cout, const int level = 0) const +std::ostream &write(std::ostream &os, const int level) const { try { // Indent, write header, newline @@ -13,16 +13,22 @@ std::ostream &write(std::ostream &os = std::cout, const int level = 0) const detail::fullName(DERIVED::namespaceName(), DERIVED::className()) ) + " " + detail::colorize_brace("{") + - (comments + /* + // fixme We may not actually want this. It's arguably largely clutter, + // and besides, someone may have used GNDStk's code generator to make + // a different library - for which "GNDS" verbiage may be confusing. + (comments ? " " + detail::colorize_comment( std::string("// GNDS: ") + DERIVED::GNDSName() ) : "" - ) + "\n" + ) + + */ + "\n" ); - if constexpr (std::is_same_v>) { + if constexpr (!hasFields) { // Consistency check assert(0 == links.size()); } else { @@ -79,7 +85,7 @@ std::ostream &write(std::ostream &os = std::cout, const int level = 0) const if constexpr (detail::hasWriteOneArg) { // DERIVED::write() doesn't take an indentation level; we handle here std::ostringstream tmp; - derived().write(tmp); + derived().write(tmp,0); if (tmp.str().size() != 0) os << indentTo(level+1); for (char c : tmp.str()) @@ -96,9 +102,9 @@ std::ostream &write(std::ostream &os = std::cout, const int level = 0) const os << std::endl; } - // BodyText, if any - if constexpr (hasBodyText) - body::write(os,level+1); + // BlockData, if any + if constexpr (hasBlockData) + BLOCKDATA::write(os,level+1); // Indent, write footer, NO newline detail::indentString( @@ -124,3 +130,128 @@ std::ostream &write(std::ostream &os = std::cout, const int level = 0) const throw; } } + + + +// ----------------------------------------------------------------------------- +// Component::write() +// Via Node, and using Node's available write() functions. +// So, autogenerated classes can directly use .write(...). +// ----------------------------------------------------------------------------- + +// write(ostream, FileType) +std::ostream &write( + std::ostream &os = std::cout, + const FileType format = FileType::null, + const bool decl = false +) const { + return Node(*this).write(os, format, decl); +} + +// write(file name, FileType) +bool write( + const std::string &filename, + FileType format = FileType::null, + const bool decl = false +) const { + return Node(*this).write(filename, format, decl); +} + +// write(ostream, string) +std::ostream &write( + std::ostream &os, + const std::string &format, + const bool decl = false +) const { + return Node(*this).write(os, format, decl); +} + +// write(file name, string) +bool write( + const std::string &filename, + const std::string &format, + const bool decl = false +) const { + return Node(*this).write(filename, format, decl); +} + + + +// ----------------------------------------------------------------------------- +// print +// Simple version of the prettyprinting write(), for user ease and familiarity. +// ----------------------------------------------------------------------------- + +/* +REMARK +A long remark about a short function. :-) + +Python has print(), and we anticipate having many Python-aware users. This, +first and foremost, is the reason for this function's existence. + +Also, importantly, we've provided a series of write() functions, above, that +mirror Node's various write()s. The Component class is designed to imbue its +derived classes with a wealth of functionality, and, in this vein, we're taking +advantage of Component's ability to convert objects of those derived classes +to Node (an ability that Component also gives us!) and to thereby make Node's +write()s available to them. + +Node's write()s can do a number of things, including, for example, writing to +XML, and also writing to an internal debug format we have in GNDStk. Given the +above write()s - which, again, are intentionally designed (and this includes +their default arguments) to mirror Node's - a call to .write() in fact ends up +going to GNDStk's Debug format. + +A user, however, printing a Component-derived class, would probably much prefer +our prettyprinting capability! Not our internal debug format. So, instead of +changing up these write() functions - making them, in effect, be inconsistent +with Node's write() functions - we'll instead give users a "simple prettyprint +to C++ standard output" call by doing exactly what we're doing here: providing +a print(), like Python does, and with a name that's different from "write" so +that no conflict exists. + +Note that our print() does something small but important: it prints a newline +at the end, which in this context a user will expect. + +Wait a minute, doesn't the main prettyprint write() already do a newline? No, +in fact, it doesn't, and as some readers may realize, it shouldn't. Just as C++ +doesn't automatically end something it prints (an integer, say, or a floating- +point number) with a newline, neither should a well-mannered system for printing +class objects. Whether a newline is appropriate, or not, depends on context. + +If you write std::cout << 1.23 << std::endl, you expect one line, with "1.23", +followed by a newline via std::endl, then the cursor ready at the beginning of +the very next line. Write std::cout << obj << std::endl, where obj is of some +user-defined type with a stream output operator that prints its own newline (but +shouldn't!), and now the cursor, after printing, will be *two* lines down, after +an intervening blank line. Which raises a question: why the different behavior? + +It may seem convenient for "large" things to print with a newline automatically, +but doing so creates inconsistent behavior between different types of printed +objects, and inconsistencies beget unpredictability. + +Moreover, Component-derived classes often contain instances of other Component- +derived classes. In the above write(), an enclosing object will place its own +newlines - only where appropriate, and NOT where not appropriate - between its +constituent parts. If those parts did their own thing in this respect, they'd +stymie the ability of the enclosing class to do the right thing. + +Just for fun, we'll give print() both const and non-const versions (one would +normally expect only a const version), and with a "builder pattern" that mirrors +that of the setters that GNDStk's code generator gives its generated classes. +This way, someone could actually prettyprint an object *while* it's being built, +builder style. Perhaps some users would find this to be useful for visualizing +what's happening as they create their objects. +*/ + +const DERIVED &print(const int level = 0) const +{ + write(std::cout,level) << std::endl; + return *static_cast(this); +} + +DERIVED &print(const int level = 0) +{ + write(std::cout,level) << std::endl; + return *static_cast(this); +} diff --git a/src/GNDStk/Component/test/Component.test.cpp b/src/GNDStk/Component/test/Component.test.cpp index 5c524cc2c..fcd2ed673 100644 --- a/src/GNDStk/Component/test/Component.test.cpp +++ b/src/GNDStk/Component/test/Component.test.cpp @@ -4,7 +4,7 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; @@ -25,7 +25,7 @@ class DerivedT : public Component static auto className() { return "DerivedT"; } static auto GNDSName() { return "none"; } static auto keys() { return std::tuple<>{}; } - DerivedT() : Component(BodyText{}) { } + DerivedT() : Component(BlockData{}) { } }; @@ -42,7 +42,7 @@ class DerivedF : public Component static auto className() { return "DerivedF"; } static auto GNDSName() { return "none"; } static auto keys() { return std::tuple<>{}; } - DerivedF() : Component(BodyText{}) { } + DerivedF() : Component(BlockData{}) { } }; @@ -71,20 +71,25 @@ class DerivedData : public Component double bar; } content; + const int &foo() const { return content.foo; } + int &foo() { return content.foo; } + const double &bar() const { return content.bar; } + double &bar() { return content.bar; } + DerivedData() : Component( - BodyText{}, - content.foo, - content.bar + BlockData{}, + foo(), + bar() ) { } DerivedData(const Node &node) : Component( - BodyText{}, - content.foo, - content.bar + BlockData{}, + foo(), + bar() ) { Component::finish(node); @@ -164,7 +169,7 @@ SCENARIO("Testing GNDStk Component") { color = false; // avoid cluttering the checked output below const std::string expected = - "DerivedData { // GNDS: data\n" + "DerivedData {\n" " foo : 12\n" " bar : 34.56\n" "} // DerivedData" @@ -187,7 +192,7 @@ SCENARIO("Testing GNDStk Component") { der << "{" " \"data\": {" - " \"attributes\": {" + " \"#attributes\": {" " \"foo\": \"12\"," " \"bar\": \"34.56\"" " }" diff --git a/src/GNDStk/Component/test/ctor.test.cpp b/src/GNDStk/Component/test/ctor.test.cpp index 74e5ed7b0..68b66ce36 100644 --- a/src/GNDStk/Component/test/ctor.test.cpp +++ b/src/GNDStk/Component/test/ctor.test.cpp @@ -2,7 +2,7 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- @@ -33,7 +33,7 @@ class DerivedNothing : public Component public: - DerivedNothing() : Component(BodyText{}) + DerivedNothing() : Component(BlockData{}) { Component::finish(); } @@ -58,13 +58,18 @@ class DerivedSomething : public Component double bar; } content; + const int &foo() const { return content.foo; } + int &foo() { return content.foo; } + const double &bar() const { return content.bar; } + double &bar() { return content.bar; } + public: DerivedSomething() : Component( - BodyText{}, - content.foo, - content.bar + BlockData{}, + foo(), + bar() ) { Component::finish(); diff --git a/src/GNDStk/Component/test/detail.test.cpp b/src/GNDStk/Component/test/detail.test.cpp index 3011e0182..c4424f3df 100644 --- a/src/GNDStk/Component/test/detail.test.cpp +++ b/src/GNDStk/Component/test/detail.test.cpp @@ -2,7 +2,7 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; @@ -58,6 +58,11 @@ struct FooBar { int foo; double bar; } content; + + const int &foo() const { return content.foo; } + int &foo() { return content.foo; } + const double &bar() const { return content.bar; } + double &bar() { return content.bar; } }; @@ -87,7 +92,7 @@ class Derived : public Component { int foo = 56; double bar = 7.8; - Derived() : Component(BodyText{},foo,bar) { } + Derived() : Component(BlockData{},foo,bar) { } }; @@ -333,7 +338,7 @@ SCENARIO("Testing Component detail:: writeComponentPart()") { // value.write(stream,level) to be called instead. // So we get the same result from each call. const std::string expected = - " Derived { // GNDS: none\n" + " Derived {\n" " foo : 56\n" " bar : 7.8\n" " } // Derived"; @@ -498,11 +503,13 @@ SCENARIO("Testing Component detail:: writeComponentPart()") { oss.str(""); writeComponentPart(oss, level=2, def, "label", maxlen=0); - CHECK(oss.str() == " label : // defaulted; is its default (2.72)"); + CHECK(oss.str() == + " label : // defaulted; is its default (2.72)"); oss.str(""); writeComponentPart(oss, level=2, def, "label", maxlen=10); - CHECK(oss.str() == " label : // defaulted; is its default (2.72)"); + CHECK(oss.str() == + " label : // defaulted; is its default (2.72)"); oss.str(""); writeComponentPart(oss, level=2, def, "", maxlen=0); @@ -658,13 +665,14 @@ SCENARIO("Testing Component detail:: getter() functions") { GIVEN("A vector of objects that have both index and label") { // look for specific index THEN("getter() based on index works properly") { - CHECK((detail::getter(vec,0,"name","class","field").value() == "0a")); - CHECK((detail::getter(vec,1,"name","class","field").value() == "1b")); - CHECK((detail::getter(vec,2,"name","class","field").value() == "2c")); - CHECK((detail::getter(vec,3,"name","class","field").value() == "3d")); - CHECK((detail::getter(vec,4,"name","class","field").value() == "4e")); + using detail::getter; + CHECK((getter(vec, 0, "name", "class", "field").value() == "0a")); + CHECK((getter(vec, 1, "name", "class", "field").value() == "1b")); + CHECK((getter(vec, 2, "name", "class", "field").value() == "2c")); + CHECK((getter(vec, 3, "name", "class", "field").value() == "3d")); + CHECK((getter(vec, 4, "name", "class", "field").value() == "4e")); try { - detail::getter(vec,100,"name","class","field"); + getter(vec, 100, "name", "class", "field"); // the above should throw, so we shouldn't get here... CHECK(false); } catch (...) { @@ -673,13 +681,14 @@ SCENARIO("Testing Component detail:: getter() functions") { // look for specific label THEN("getter() based on label works properly") { - CHECK((detail::getter(vec,"a","name","class","field").value() == "0a")); - CHECK((detail::getter(vec,"b","name","class","field").value() == "1b")); - CHECK((detail::getter(vec,"c","name","class","field").value() == "2c")); - CHECK((detail::getter(vec,"d","name","class","field").value() == "3d")); - CHECK((detail::getter(vec,"e","name","class","field").value() == "4e")); + using detail::getter; + CHECK((getter(vec, "a", "name", "class", "field").value() == "0a")); + CHECK((getter(vec, "b", "name", "class", "field").value() == "1b")); + CHECK((getter(vec, "c", "name", "class", "field").value() == "2c")); + CHECK((getter(vec, "d", "name", "class", "field").value() == "3d")); + CHECK((getter(vec, "e", "name", "class", "field").value() == "4e")); try { - detail::getter(vec,"z","name","class","field"); + getter(vec, "z", "name", "class", "field"); // the above should throw, so we shouldn't get here... CHECK(false); } catch (...) { @@ -698,13 +707,14 @@ SCENARIO("Testing Component detail:: getter() functions") { // look for specific index THEN("getter() based on index works properly") { - CHECK((detail::getter(opt,0UL,"name","class","field").value() == "0a")); - CHECK((detail::getter(opt,1UL,"name","class","field").value() == "1b")); - CHECK((detail::getter(opt,2UL,"name","class","field").value() == "2c")); - CHECK((detail::getter(opt,3UL,"name","class","field").value() == "3d")); - CHECK((detail::getter(opt,4UL,"name","class","field").value() == "4e")); + using detail::getter; + CHECK((getter(opt, 0UL, "name", "class", "field").value() == "0a")); + CHECK((getter(opt, 1UL, "name", "class", "field").value() == "1b")); + CHECK((getter(opt, 2UL, "name", "class", "field").value() == "2c")); + CHECK((getter(opt, 3UL, "name", "class", "field").value() == "3d")); + CHECK((getter(opt, 4UL, "name", "class", "field").value() == "4e")); try { - detail::getter(opt,100UL,"name","class","field"); + getter(opt, 100UL, "name", "class", "field"); // the above should throw, so we shouldn't get here... CHECK(false); } catch (...) { @@ -713,13 +723,14 @@ SCENARIO("Testing Component detail:: getter() functions") { // look for specific label THEN("getter() based on label works properly") { - CHECK((detail::getter(opt,"a","name","class","field").value() == "0a")); - CHECK((detail::getter(opt,"b","name","class","field").value() == "1b")); - CHECK((detail::getter(opt,"c","name","class","field").value() == "2c")); - CHECK((detail::getter(opt,"d","name","class","field").value() == "3d")); - CHECK((detail::getter(opt,"e","name","class","field").value() == "4e")); + using detail::getter; + CHECK((getter(opt, "a", "name", "class", "field").value() == "0a")); + CHECK((getter(opt, "b", "name", "class", "field").value() == "1b")); + CHECK((getter(opt, "c", "name", "class", "field").value() == "2c")); + CHECK((getter(opt, "d", "name", "class", "field").value() == "3d")); + CHECK((getter(opt, "e", "name", "class", "field").value() == "4e")); try { - detail::getter(opt,"z","name","class","field"); + getter(opt, "z", "name", "class", "field"); // the above should throw, so we shouldn't get here... CHECK(false); } catch (...) { @@ -739,7 +750,7 @@ SCENARIO("Testing Component detail:: getter() functions") { // look for specific index THEN("getter() based on index works properly") { try { - detail::getter(opt,0,"name","class","field"); + detail::getter(opt, 0, "name", "class", "field"); // the above should throw, so we shouldn't get here... CHECK(false); } catch (...) { @@ -749,7 +760,7 @@ SCENARIO("Testing Component detail:: getter() functions") { // look for specific label THEN("getter() based on label works properly") { try { - detail::getter(opt,"a","name","class","field"); + detail::getter(opt, "a", "name", "class", "field"); // the above should throw, so we shouldn't get here... CHECK(false); } catch (...) { diff --git a/src/GNDStk/Component/test/finish.test.cpp b/src/GNDStk/Component/test/finish.test.cpp index 59d7edab3..330a0f977 100644 --- a/src/GNDStk/Component/test/finish.test.cpp +++ b/src/GNDStk/Component/test/finish.test.cpp @@ -2,13 +2,13 @@ #include "catch.hpp" #include "GNDStk.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- // DerivedValue -// Has body text +// Has block data // ----------------------------------------------------------------------------- namespace test { @@ -18,13 +18,16 @@ struct IndexStruct { struct { std::size_t index; } content; - IndexStruct(const std::size_t i = 0) { content.index = i; } + const std::size_t &index() const { return content.index; } + std::size_t &index() { return content.index; } + + IndexStruct(const std::size_t i = 0) { index() = i; } IndexStruct(const Node &) : IndexStruct(0) { } }; inline bool operator==(const IndexStruct &one, const IndexStruct &two) { - return one.content.index == two.content.index; + return one.index() == two.index(); } @@ -64,7 +67,8 @@ class DerivedValue : public Component // one of the tests involves checking these struct { // Initialize these to specific values, so that we can ensure that - // Component's finish() functions properly call body::pullFromDerived() + // Component's finish() functions properly call + // BLOCKDATA::pullFromDerived() int length = 11; int start = 3; std::string valueType = "foobar"; @@ -75,6 +79,20 @@ class DerivedValue : public Component {{3,2,17,7,5,9,13,11}}; } content; + const int &length() const { return content.length; } + int &length() { return content.length; } + + const int &start() const { return content.start; } + int &start() { return content.start; } + + const std::string &valueType() const { return content.valueType; } + std::string &valueType() { return content.valueType; } + + const std::optional> &indices() const + { return content.indices; } + std::optional> &indices() + { return content.indices; } + private: // construct functions @@ -106,8 +124,8 @@ class DerivedValue : public Component // ctor: default DerivedValue() : Component( - BodyText{}, - content.length, content.start, content.valueType, content.indices + BlockData{}, + length(), start(), valueType(), indices() ) { // finish() @@ -118,7 +136,7 @@ class DerivedValue : public Component DerivedValue(const DerivedValue &other) : Component{ other, - content.length, content.start, content.valueType, content.indices + length(), start(), valueType(), indices() }, content{other.content} { @@ -129,8 +147,8 @@ class DerivedValue : public Component // ctor: node DerivedValue(const Node &node) : Component{ - BodyText{}, - content.length, content.start, content.valueType, content.indices + BlockData{}, + length(), start(), valueType(), indices() } { // finish(node) @@ -140,8 +158,8 @@ class DerivedValue : public Component // ctor: vector DerivedValue(const std::vector &vec) : Component{ - BodyText{}, - content.length, content.start, content.valueType, content.indices + BlockData{}, + length(), start(), valueType(), indices() } { // finish(vector) @@ -155,7 +173,7 @@ class DerivedValue : public Component // ----------------------------------------------------------------------------- // DerivedPlain -// Does not have body text +// Does not have block data // ----------------------------------------------------------------------------- namespace test { @@ -165,17 +183,20 @@ struct LabelStruct { struct { std::string label; } content; + const std::string &label() const { return content.label; } + std::string &label() { return content.label; } + // apparently need a char* ctor for initializer-list initialization to work - LabelStruct(const char *const str = "") { content.label = str; } + LabelStruct(const char *const str = "") { label() = str; } LabelStruct(const Node &node) { - content.label = node(std::string{}/Meta<>("label")); + label() = node(std::string{}/Meta<>("label")); } }; inline bool operator==(const LabelStruct &one, const LabelStruct &two) { - return one.content.label == two.content.label; + return one.label() == two.label(); } @@ -218,6 +239,17 @@ class DerivedPlain : public Component {{"bc","a","p","efg","d","hi","no","jklm"}}; } content; + const int &foo() const { return content.foo; } + int &foo() { return content.foo; } + + const double &bar() const { return content.bar; } + double &bar() { return content.bar; } + + const std::optional> &labels() const + { return content.labels; } + std::optional> &labels() + { return content.labels; } + private: // construct functions @@ -243,8 +275,8 @@ class DerivedPlain : public Component // ctor: default DerivedPlain() : Component( - BodyText{}, - content.foo, content.bar, content.labels + BlockData{}, + foo(), bar(), labels() ) { // finish() @@ -255,7 +287,7 @@ class DerivedPlain : public Component DerivedPlain(const DerivedPlain &other) : Component{ other, - content.foo, content.bar, content.labels + foo(), bar(), labels() }, content{other.content} { @@ -266,8 +298,8 @@ class DerivedPlain : public Component // ctor: node DerivedPlain(const Node &node) : Component{ - BodyText{}, - content.foo, content.bar, content.labels + BlockData{}, + foo(), bar(), labels() } { // finish(node) @@ -289,7 +321,7 @@ class DerivedPlain : public Component SCENARIO("Component finish()") { - GIVEN("A component-derived class that has body text") { + GIVEN("A component-derived class that has block data") { const std::vector sorted = {{2,3,5,7,9,11,13,17}}; @@ -300,15 +332,15 @@ SCENARIO("Component finish()") { // Ensure that finish() called the construct() in the derived class... CHECK(test::construct1DerivedValue == true); - // Ensure that finish() did a BodyText::pullFromDerived() + // Ensure that finish() did a BlockData::pullFromDerived() CHECK(d.length() == 11); CHECK(d.start() == 3); CHECK(d.valueType() == "foobar"); // Ensure that finish() did a sort() - CHECK(d.content.indices.has_value() == true); - CHECK(d.content.indices->size() == 8); - CHECK(*d.content.indices == sorted); + CHECK(d.indices().has_value() == true); + CHECK(d.indices()->size() == 8); + CHECK(*d.indices() == sorted); } // ctor: copy @@ -340,9 +372,9 @@ SCENARIO("Component finish()") { CHECK(d.valueType() == "foobar"); // Ensure that finish() did a sort() - CHECK(d.content.indices.has_value() == true); - CHECK(d.content.indices->size() == 8); - CHECK(*d.content.indices == sorted); + CHECK(d.indices().has_value() == true); + CHECK(d.indices()->size() == 8); + CHECK(*d.indices() == sorted); } // ctor: from node @@ -357,7 +389,7 @@ SCENARIO("Component finish()") { test::DerivedValue d(node); CHECK(test::construct3DerivedValue == true); - // Here, the following values in the underlying BodyText should + // Here, the following values in the underlying BlockData should // reflect those that were brought in through the above string. CHECK(d.length() == 10); CHECK(d.start() == 2); @@ -375,9 +407,9 @@ SCENARIO("Component finish()") { CHECK(d.get(8) == 0); CHECK(d.get(9) == 0); - // The node from which we read had body text, not child nodes, + // The node from which we read had block data, not child nodes, // and thus would give us nothing for (std::optional) indices... - CHECK(d.content.indices.has_value() == false); + CHECK(d.indices().has_value() == false); } // ctor: from vector @@ -388,7 +420,7 @@ SCENARIO("Component finish()") { CHECK(test::construct4DerivedValue == true); // Here, the finish(vector) function was called, which in turn called - // BodyText's operator=(vector), which sets the following according + // BlockData's operator=(vector), which sets the following according // to what's actually in the vector CHECK(d.length() == 3); CHECK(d.start() == 0); // <== always the case in this context @@ -400,22 +432,22 @@ SCENARIO("Component finish()") { CHECK(Approx(d.get(1)) == 2.71828); CHECK(Approx(d.get(2)) == 1.41421); - // And, BodyText's operator=(vector) as mentioned above should also + // And, BlockData's operator=(vector) as mentioned above should also // have changed the corresponding values back up in the derived class - CHECK(d.content.length == 3); - CHECK(d.content.start == 0); - CHECK(d.content.valueType == "Float64"); + CHECK(d.length() == 3); + CHECK(d.start() == 0); + CHECK(d.valueType() == "Float64"); // Ensure that finish() did a sort() - CHECK(d.content.indices.has_value() == true); - CHECK(d.content.indices->size() == 8); - CHECK(*d.content.indices == sorted); + CHECK(d.indices().has_value() == true); + CHECK(d.indices()->size() == 8); + CHECK(*d.indices() == sorted); } } // GIVEN - GIVEN("A component-derived class that does not have body text") { + GIVEN("A component-derived class that does not have block data") { const std::vector sorted = {{"a","bc","d","efg","hi","jklm","no","p"}}; @@ -425,9 +457,9 @@ SCENARIO("Component finish()") { test::DerivedPlain d; CHECK(test::construct1DerivedPlain == true); - CHECK(d.content.labels.has_value() == true); - CHECK(d.content.labels->size() == 8); - CHECK(*d.content.labels == sorted); + CHECK(d.labels().has_value() == true); + CHECK(d.labels()->size() == 8); + CHECK(*d.labels() == sorted); } // ctor: copy @@ -438,9 +470,9 @@ SCENARIO("Component finish()") { test::DerivedPlain d(dfrom); CHECK(test::construct2DerivedPlain == true); - CHECK(d.content.labels.has_value() == true); - CHECK(d.content.labels->size() == 8); - CHECK(*d.content.labels == sorted); + CHECK(d.labels().has_value() == true); + CHECK(d.labels()->size() == 8); + CHECK(*d.labels() == sorted); } // ctor: from node, case 1 @@ -454,7 +486,7 @@ SCENARIO("Component finish()") { test::DerivedPlain d(node); CHECK(test::construct3DerivedPlain == true); - CHECK(d.content.labels.has_value() == false); + CHECK(d.labels().has_value() == false); } // ctor: from node, case 2 @@ -472,10 +504,10 @@ SCENARIO("Component finish()") { test::DerivedPlain d(node); CHECK(test::construct3DerivedPlain == true); - CHECK(d.content.labels.has_value() == true); - CHECK(d.content.labels->size() == 4); + CHECK(d.labels().has_value() == true); + CHECK(d.labels()->size() == 4); CHECK(( - *d.content.labels == + *d.labels() == std::vector{{"abc","def","ghi","jkl"}} )); } diff --git a/src/GNDStk/Component/test/fromNode.test.cpp b/src/GNDStk/Component/test/fromNode.test.cpp index 55a9e5cae..f417bf0a4 100644 --- a/src/GNDStk/Component/test/fromNode.test.cpp +++ b/src/GNDStk/Component/test/fromNode.test.cpp @@ -3,9 +3,12 @@ #include "GNDStk.hpp" #include "prototype.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; using namespace GNDStk::proto; +#include "GNDStk/test/keys.hpp" +using namespace basic; + // ----------------------------------------------------------------------------- // DISCUSSION @@ -19,9 +22,9 @@ Component does the following: - Calls Component's fromNode() function to read known fields from the Node into the derived class object. - - If the Node has "body text," syncs certain fields in the BodyText (base + - If the Node has block data, syncs certain fields in the BlockData (base of Component) class, with fields in the derived class. Then, converts a - raw body-text string into a vector of values. + raw block data string into a vector of values. - Performs a sort() of derived-class vectors that are known to Component, and that have an index and/or a label. @@ -35,7 +38,7 @@ The tests in this file make use of a particular GNDS file with a reactionSuite. We use a prototype ReactionSuite class (with content that's pared down from what's in the full GNDS spec) that's derived from Component. -The reaction suite itself doesn't (directly) have any body text or sort-able +The reaction suite itself doesn't (directly) have any block data or sort-able fields, and doesn't have a construct(). So, our first test (1) constructs from a Node, (2) uses fromNode() to read from @@ -53,8 +56,8 @@ give different results: the former sorts, the latter doesn't. After the results from (2) are sorted, however, the two Reactions objects are the same. Finally, our third test works with Values objects. Objects of this type have -body text. Construction from Node does a get(), which takes the original, raw -body text in the Node, and makes a vector (in this case vector) from +block data. Construction from Node does a get(), which takes the original, raw +block data in the Node, and makes a vector (in this case vector) from it. fromNode() doesn't do that until, and unless, we ask for it by calling get() directly. Values objects with only raw text write differently than those with text that was processed into a vector. So, this test first ensures that the two @@ -162,7 +165,7 @@ SCENARIO("Component fromNode()") { oss2 << values2; CHECK(oss1.str() != oss2.str()); // not equal (yet) - // *** Apply get() *** to transform the raw string of body-text + // *** Apply get() *** to transform the raw string of block data // values into a vector values2.get(); oss2.str(""); diff --git a/src/GNDStk/Component/test/getter.test.cpp b/src/GNDStk/Component/test/getter.test.cpp index 46e815e43..92031ac0c 100644 --- a/src/GNDStk/Component/test/getter.test.cpp +++ b/src/GNDStk/Component/test/getter.test.cpp @@ -3,7 +3,7 @@ #include "GNDStk.hpp" #include "indexnlabel.hpp" -using namespace njoy::GNDStk::core; +using namespace njoy::GNDStk; // ----------------------------------------------------------------------------- @@ -56,7 +56,7 @@ class TestGetter : public Component // constructor: default // ------------------------ - TestGetter() : Component{ BodyText{} } + TestGetter() : Component{ BlockData{} } { // Component::finish(); = not needed here } @@ -98,15 +98,24 @@ class TestGetter : public Component CHECK( getter(vecIndexLabel,7,"vecIndexLabel").value() == "7 (seven)" ); // re: vecIndexLabel, lookup by label - CHECK( getter(vecIndexLabel,"five","vecIndexLabel").index() == 5 ); - CHECK( getter(vecIndexLabel,"five","vecIndexLabel").label() == "five" ); - CHECK( getter(vecIndexLabel,"five","vecIndexLabel").value() == "5 (five)" ); - CHECK( getter(vecIndexLabel,"six","vecIndexLabel").index() == 6 ); - CHECK( getter(vecIndexLabel,"six","vecIndexLabel").label() == "six" ); - CHECK( getter(vecIndexLabel,"six","vecIndexLabel").value() == "6 (six)" ); - CHECK( getter(vecIndexLabel,"seven","vecIndexLabel").index() == 7 ); - CHECK( getter(vecIndexLabel,"seven","vecIndexLabel").label() == "seven" ); - CHECK( getter(vecIndexLabel,"seven","vecIndexLabel").value() == "7 (seven)" ); + CHECK( getter(vecIndexLabel,"five","vecIndexLabel").index() + == 5 ); + CHECK( getter(vecIndexLabel,"five","vecIndexLabel").label() + == "five" ); + CHECK( getter(vecIndexLabel,"five","vecIndexLabel").value() + == "5 (five)" ); + CHECK( getter(vecIndexLabel,"six","vecIndexLabel").index() + == 6 ); + CHECK( getter(vecIndexLabel,"six","vecIndexLabel").label() + == "six" ); + CHECK( getter(vecIndexLabel,"six","vecIndexLabel").value() + == "6 (six)" ); + CHECK( getter(vecIndexLabel,"seven","vecIndexLabel").index() + == 7 ); + CHECK( getter(vecIndexLabel,"seven","vecIndexLabel").label() + == "seven" ); + CHECK( getter(vecIndexLabel,"seven","vecIndexLabel").value() + == "7 (seven)" ); } // non-const @@ -156,27 +165,36 @@ class TestGetter : public Component IndexLabel{ 11, "eleven", "11 (eleven)" }; // verify the new value - CHECK( getter(vecIndexLabel,11,"vecIndexLabel").index() == 11 ); - CHECK( getter(vecIndexLabel,11,"vecIndexLabel").label() == "eleven" ); - CHECK( getter(vecIndexLabel,11,"vecIndexLabel").value() == "11 (eleven)" ); + CHECK( getter(vecIndexLabel,11,"vecIndexLabel").index() + == 11 ); + CHECK( getter(vecIndexLabel,11,"vecIndexLabel").label() + == "eleven" ); + CHECK( getter(vecIndexLabel,11,"vecIndexLabel").value() + == "11 (eleven)" ); // ------------------------ // re: vecIndexLabel, lookup by label // ------------------------ // verify an existing value - CHECK( getter(vecIndexLabel,"six","vecIndexLabel").index() == 6 ); - CHECK( getter(vecIndexLabel,"six","vecIndexLabel").label() == "six" ); - CHECK( getter(vecIndexLabel,"six","vecIndexLabel").value() == "6 (six)" ); + CHECK( getter(vecIndexLabel,"six","vecIndexLabel").index() + == 6 ); + CHECK( getter(vecIndexLabel,"six","vecIndexLabel").label() + == "six" ); + CHECK( getter(vecIndexLabel,"six","vecIndexLabel").value() + == "6 (six)" ); // change getter(vecIndexLabel,"six","vecIndexLabel") = IndexLabel{ 13, "thirteen", "13 (thirteen)" }; // verify new value - CHECK( getter(vecIndexLabel,"thirteen","vecIndexLabel").index() == 13); - CHECK( getter(vecIndexLabel,"thirteen","vecIndexLabel").label() == "thirteen" ); - CHECK( getter(vecIndexLabel,"thirteen","vecIndexLabel").value() == "13 (thirteen)" ); + CHECK( getter(vecIndexLabel,"thirteen","vecIndexLabel").index() + == 13); + CHECK( getter(vecIndexLabel,"thirteen","vecIndexLabel").label() + == "thirteen" ); + CHECK( getter(vecIndexLabel,"thirteen","vecIndexLabel").value() + == "13 (thirteen)" ); } // ------------------------ @@ -223,14 +241,20 @@ class TestGetter : public Component CHECK( getter