aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-03-28 09:14:31 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-03-28 16:03:35 +0200
commit3ce44330cca9dbc4314feebb27403ebc3175b6c2 (patch)
tree8c1f27442f3b2dafaec3ba50baaca3d5fd63dca7
parent9d0d078ff297138622cd2f3f1076f5984395e42b (diff)
New variable architecture
-rw-r--r--build2/b.cxx91
-rw-r--r--build2/bin/module.cxx6
-rw-r--r--build2/bin/rule.cxx4
-rw-r--r--build2/cli/module.cxx10
-rw-r--r--build2/cli/rule.cxx2
-rw-r--r--build2/config/operation.cxx32
-rw-r--r--build2/config/utility.cxx2
-rw-r--r--build2/cxx/compile.cxx16
-rw-r--r--build2/cxx/link.cxx30
-rw-r--r--build2/cxx/module.cxx12
-rw-r--r--build2/cxx/utility.txx2
-rw-r--r--build2/dist/operation.cxx14
-rw-r--r--build2/dump.cxx5
-rw-r--r--build2/file10
-rw-r--r--build2/file.cxx212
-rw-r--r--build2/install/module.cxx2
-rw-r--r--build2/install/rule.cxx29
-rw-r--r--build2/module.cxx10
-rw-r--r--build2/name83
-rw-r--r--build2/name.cxx6
-rw-r--r--build2/name.ixx42
-rw-r--r--build2/parser.cxx36
-rw-r--r--build2/scope5
-rw-r--r--build2/target5
-rw-r--r--build2/target.txx2
-rw-r--r--build2/test/rule.cxx10
-rw-r--r--build2/types11
-rw-r--r--build2/utility6
-rw-r--r--build2/utility.cxx10
-rw-r--r--build2/utility.ixx1
-rw-r--r--build2/variable752
-rw-r--r--build2/variable.cxx604
-rw-r--r--build2/variable.ixx525
-rw-r--r--build2/variable.txx401
34 files changed, 1649 insertions, 1339 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 2f94ad3..f2cb9eb 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -482,51 +482,61 @@ main (int argc, char* argv[])
//
scope& rs (create_root (out_root, src_root));
- bootstrap_out (rs);
+ bool bootstrapped (build2::bootstrapped (rs));
- // See if the bootstrap process set/changed src_root.
- //
- value& v (rs.assign ("src_root"));
-
- if (v)
+ if (!bootstrapped)
{
- // If we also have src_root specified by the user, make
- // sure they match.
- //
- const dir_path& p (as<dir_path> (v));
+ bootstrap_out (rs);
- if (src_root.empty ())
- src_root = p;
- else if (src_root != p)
- fail << "bootstrapped src_root " << p << " does not match "
- << "specified " << src_root;
- }
- else
- {
- // Neither bootstrap nor the user produced src_root.
+ // See if the bootstrap process set/changed src_root.
//
- if (src_root.empty ())
+ value& v (rs.assign ("src_root"));
+
+ if (v)
{
- // If it also wasn't explicitly specified, see if it is
- // the same as out_root.
+ // If we also have src_root specified by the user, make
+ // sure they match.
//
- if (is_src_root (out_root))
- src_root = out_root;
- else
+ const dir_path& p (cast<dir_path> (v));
+
+ if (src_root.empty ())
+ src_root = p;
+ else if (src_root != p)
+ fail << "bootstrapped src_root " << p << " does not match "
+ << "specified " << src_root;
+ }
+ else
+ {
+ // Neither bootstrap nor the user produced src_root.
+ //
+ if (src_root.empty ())
{
- // If not, then assume we are running from src_base
- // and calculate src_root based on out_root/out_base.
+ // If it also wasn't explicitly specified, see if it is
+ // the same as out_root.
//
- src_base = work;
- src_root = src_base.directory (out_base.leaf (out_root));
- guessing = true;
+ if (is_src_root (out_root))
+ src_root = out_root;
+ else
+ {
+ // If not, then assume we are running from src_base
+ // and calculate src_root based on out_root/out_base.
+ //
+ src_base = work;
+ src_root = src_base.directory (out_base.leaf (out_root));
+ guessing = true;
+ }
}
+
+ v = src_root;
}
- v = src_root;
- }
+ setup_root (rs);
- setup_root (rs);
+ // Now that we have src_root, load the src_root bootstrap file,
+ // if there is one.
+ //
+ bootstrapped = bootstrap_src (rs);
+ }
// At this stage we should have both roots and out_base figured
// out. If src_base is still undetermined, calculate it.
@@ -534,25 +544,20 @@ main (int argc, char* argv[])
if (src_base.empty ())
src_base = src_root / out_base.leaf (out_root);
- // Now that we have src_root, load the src_root bootstrap file,
- // if there is one.
- //
- bool bootstrapped (bootstrap_src (rs));
-
// Check that out_root that we have found is the innermost root
// for this project. If it is not, then it means we are trying
// to load a disfigured sub-project and that we do not support.
// Why don't we support it? Because things are already complex
// enough here.
//
+ // Note that the subprojects variable has already been processed
+ // and converted to a map by the bootstrap_src() call above.
+ //
if (auto l = rs.vars["subprojects"])
{
- for (const name& n: *l)
+ for (const auto& p: cast<subprojects> (*l))
{
- if (n.pair)
- continue; // Skip project names.
-
- if (out_base.sub (out_root / n.dir))
+ if (out_base.sub (out_root / p.second))
fail << tn << " is in a subproject of " << out_root <<
info << "explicitly specify src_base for this target";
}
diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx
index 702e762..496b1da 100644
--- a/build2/bin/module.cxx
+++ b/build2/bin/module.cxx
@@ -156,7 +156,7 @@ namespace build2
// See the cxx module for details on merging.
//
if (const value& v = config::optional (r, "config.bin.rpath"))
- b.assign ("bin.rpath") += as<strings> (v);
+ b.assign ("bin.rpath") += cast<strings> (v);
// config.bin.ar
// config.bin.ranlib
@@ -170,8 +170,8 @@ namespace build2
auto p (config::required (r, "config.bin.ar", "ar"));
auto& v (config::optional (r, "config.bin.ranlib"));
- const path& ar (path (as<string> (p.first))); // @@ VAR
- const path& ranlib (v ? path (as<string> (v)) : path ()); // @@ VAR
+ const path& ar (path (cast<string> (p.first))); // @@ VAR
+ const path& ranlib (v ? path (cast<string> (v)) : path ()); // @@ VAR
bin_info bi (guess (ar, ranlib));
diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx
index 4b1da8f..d74dd65 100644
--- a/build2/bin/rule.cxx
+++ b/build2/bin/rule.cxx
@@ -46,7 +46,7 @@ namespace build2
// Get the library type to build. If not set for a target, this
// should be configured at the project scope by init_lib().
//
- const string& type (as<string> (*t["bin.lib"]));
+ const string& type (cast<string> (*t["bin.lib"]));
bool ar (type == "static" || type == "both");
bool so (type == "shared" || type == "both");
@@ -121,7 +121,7 @@ namespace build2
// prerequisite vs prerequisite_target.
//
//
- const string& type (as<string> (*t["bin.lib"]));
+ const string& type (cast<string> (*t["bin.lib"]));
bool ar (type == "static" || type == "both");
bool so (type == "shared" || type == "both");
diff --git a/build2/cli/module.cxx b/build2/cli/module.cxx
index 6497766..907bd0b 100644
--- a/build2/cli/module.cxx
+++ b/build2/cli/module.cxx
@@ -45,7 +45,7 @@ namespace build2
{
auto l (base["cxx.loaded"]);
- if (!l || !as<bool> (*l))
+ if (!l || !cast<bool> (*l))
fail (loc) << "cxx module must be loaded before cli";
}
@@ -90,7 +90,7 @@ namespace build2
{
auto l (root["config.cli.configured"]);
- if (l && !as<bool> (*l))
+ if (l && !cast<bool> (*l))
return false;
}
@@ -175,7 +175,7 @@ namespace build2
else
{
auto p (config::required (root, "config.cli", cli));
- assert (p.second && as<string> (p.first) == cli);
+ assert (p.second && cast<string> (p.first) == cli);
}
}
else
@@ -186,7 +186,7 @@ namespace build2
//
if (p.second)
{
- cli = as<string> (p.first).c_str ();
+ cli = cast<string> (p.first).c_str ();
ver = test (cli);
if (ver.empty ())
@@ -209,7 +209,7 @@ namespace build2
// this merging semantics and some of its tricky aspects.
//
if (const value& v = config::optional (root, "config.cli.options"))
- base.assign ("cli.options") += as<strings> (v);
+ base.assign ("cli.options") += cast<strings> (v);
// Register our rules.
//
diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx
index bf52993..243c6aa 100644
--- a/build2/cli/rule.cxx
+++ b/build2/cli/rule.cxx
@@ -244,7 +244,7 @@ namespace build2
path rels (relative (s->path ()));
scope& rs (t.root_scope ());
- const string& cli (as<string> (*rs["config.cli"]));
+ const string& cli (cast<string> (*rs["config.cli"]));
cstrings args {cli.c_str ()};
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
index 6199f8b..8047e61 100644
--- a/build2/config/operation.cxx
+++ b/build2/config/operation.cxx
@@ -86,7 +86,7 @@ namespace build2
if (auto l = root.vars["amalgamation"])
{
- const dir_path& d (as<dir_path> (*l));
+ const dir_path& d (cast<dir_path> (*l));
ofs << "# Base configuration inherited from " << d << endl
<< "#" << endl;
@@ -95,6 +95,8 @@ namespace build2
// Save all the variables in the config namespace that are set
// on the project's root scope.
//
+ names storage;
+
for (auto p (root.vars.find_namespace ("config"));
p.first != p.second;
++p.first)
@@ -115,7 +117,7 @@ namespace build2
if (n.size () > 11 &&
n.compare (n.size () - 11, 11, ".configured") == 0)
{
- if (val == nullptr || as<bool> (val))
+ if (val == nullptr || cast<bool> (val))
continue;
}
@@ -132,14 +134,11 @@ namespace build2
if (val)
{
- ofs << var.name << " = " << val.data_ << endl;
- //text << var.name << " = " << val.data_;
+ storage.clear ();
+ ofs << var.name << " = " << reverse (val, storage) << endl;
}
else
- {
ofs << var.name << " = #[null]" << endl; // @@ TODO: [null]
- //text << var.name << " = [null]";
- }
}
}
catch (const ofstream::failure&)
@@ -189,7 +188,7 @@ namespace build2
//
if (auto l = root.vars["subprojects"])
{
- for (auto p: as<subprojects> (*l))
+ for (auto p: cast<subprojects> (*l))
{
const dir_path& pd (p.second);
dir_path out_nroot (out_root / pd);
@@ -314,7 +313,7 @@ namespace build2
//
if (auto l = root.vars["subprojects"])
{
- for (auto p: as<subprojects> (*l))
+ for (auto p: cast<subprojects> (*l))
{
const dir_path& pd (p.second);
@@ -325,16 +324,19 @@ namespace build2
// The same logic for src_root as in create_bootstrap_inner().
//
scope& nroot (create_root (out_nroot, dir_path ()));
- bootstrap_out (nroot);
- value& val (nroot.assign ("src_root"));
+ if (!bootstrapped (nroot))
+ {
+ bootstrap_out (nroot);
- if (!val)
- val = is_src_root (out_nroot) ? out_nroot : (src_root / pd);
+ value& val (nroot.assign ("src_root"));
- setup_root (nroot);
+ if (!val)
+ val = is_src_root (out_nroot) ? out_nroot : (src_root / pd);
- bootstrap_src (nroot);
+ setup_root (nroot);
+ bootstrap_src (nroot);
+ }
m = disfigure_project (a, nroot) || m;
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
index d27e1ff..e96a896 100644
--- a/build2/config/utility.cxx
+++ b/build2/config/utility.cxx
@@ -43,7 +43,7 @@ namespace build2
if (v && !v.empty ())
{
- dir_path& d (as<dir_path> (v));
+ dir_path& d (cast<dir_path> (v));
if (d.relative ())
{
diff --git a/build2/cxx/compile.cxx b/build2/cxx/compile.cxx
index df2b798..a2b00de 100644
--- a/build2/cxx/compile.cxx
+++ b/build2/cxx/compile.cxx
@@ -130,7 +130,7 @@ namespace build2
//
if (a == perform_update_id)
{
- const string& sys (as<string> (*rs["cxx.target.system"]));
+ const string& sys (cast<string> (*rs["cxx.target.system"]));
// The cached prerequisite target should be the same as what is in
// t.prerequisite_targets since we used standard search() and match()
@@ -165,7 +165,7 @@ namespace build2
// Then the compiler checksum.
//
- if (dd.expect (as<string> (*rs["cxx.checksum"])) != nullptr)
+ if (dd.expect (cast<string> (*rs["cxx.checksum"])) != nullptr)
l4 ([&]{trace << "compiler mismatch forcing update of " << t;});
// Then the options checksum.
@@ -237,7 +237,7 @@ namespace build2
-> const target_type*
{
if (auto l = s.lookup (tt, n, var))
- if (as<string> (*l) == e)
+ if (cast<string> (*l) == e)
return &tt;
return nullptr;
@@ -283,7 +283,7 @@ namespace build2
if (auto l = t[var])
{
- const auto& v (as<strings> (*l));
+ const auto& v (cast<strings> (*l));
for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
{
@@ -461,8 +461,8 @@ namespace build2
auto init_args = [&t, &s, &rs, &args, &cxx_std] ()
{
- const string& cxx (as<string> (*rs["config.cxx"]));
- const string& sys (as<string> (*rs["cxx.target.system"]));
+ const string& cxx (cast<string> (*rs["config.cxx"]));
+ const string& sys (cast<string> (*rs["cxx.target.system"]));
args.push_back (cxx.c_str ());
@@ -923,8 +923,8 @@ namespace build2
path rels (relative (s->path ()));
scope& rs (t.root_scope ());
- const string& cxx (as<string> (*rs["config.cxx"]));
- const string& sys (as<string> (*rs["cxx.target.system"]));
+ const string& cxx (cast<string> (*rs["config.cxx"]));
+ const string& sys (cast<string> (*rs["cxx.target.system"]));
cstrings args {cxx.c_str ()};
diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx
index 5ed36b7..ea20a47 100644
--- a/build2/cxx/link.cxx
+++ b/build2/cxx/link.cxx
@@ -42,7 +42,7 @@ namespace build2
case type::so: var = "bin.libso.lib"; break;
}
- const auto& v (as<strings> (*t[var]));
+ const auto& v (cast<strings> (*t[var]));
return v[0] == "shared"
? v.size () > 1 && v[1] == "static" ? order::so_a : order::so
: v.size () > 1 && v[1] == "shared" ? order::a_so : order::a;
@@ -52,7 +52,7 @@ namespace build2
link_member (bin::lib& l, order lo)
{
bool lso (true);
- const string& at (as<string> (*l["bin.lib"])); // Available types.
+ const string& at (cast<string> (*l["bin.lib"])); // Available types.
switch (lo)
{
@@ -92,7 +92,7 @@ namespace build2
//
if (auto l = bs["cxx.loptions"])
{
- const auto& v (as<strings> (*l));
+ const auto& v (cast<strings> (*l));
for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
{
@@ -123,7 +123,7 @@ namespace build2
cstrings args;
string std_storage;
- args.push_back (as<string> (*rs["config.cxx"]).c_str ());
+ args.push_back (cast<string> (*rs["config.cxx"]).c_str ());
append_options (args, bs, "cxx.coptions");
append_std (args, bs, std_storage);
append_options (args, bs, "cxx.loptions");
@@ -218,7 +218,7 @@ namespace build2
return p.target;
scope& rs (*p.scope.root_scope ());
- const string& sys (as<string> (*rs["cxx.target.system"]));
+ const string& sys (cast<string> (*rs["cxx.target.system"]));
bool l (p.is_a<lib> ());
const string* ext (l ? nullptr : p.ext); // Only for liba/libso.
@@ -483,7 +483,7 @@ namespace build2
path_target& t (static_cast<path_target&> (xt));
scope& rs (t.root_scope ());
- const string& sys (as<string> (*rs["cxx.target.system"]));
+ const string& sys (cast<string> (*rs["cxx.target.system"]));
type lt (link_type (t));
bool so (lt == type::so);
@@ -510,7 +510,7 @@ namespace build2
case type::so:
{
auto l (t["bin.libprefix"]);
- const char* p (l ? as<string> (*l).c_str () : "lib");
+ const char* p (l ? cast<string> (*l).c_str () : "lib");
const char* e;
if (lt == type::a)
@@ -769,7 +769,7 @@ namespace build2
bool up (execute_prerequisites (a, t, t.mtime ()));
scope& rs (t.root_scope ());
- const string& sys (as<string> (*rs["cxx.target.system"]));
+ const string& sys (cast<string> (*rs["cxx.target.system"]));
// Check/update the dependency database.
//
@@ -793,10 +793,10 @@ namespace build2
const char* rl (
ranlib
- ? as<string> (*rs["bin.ranlib.checksum"]).c_str ()
+ ? cast<string> (*rs["bin.ranlib.checksum"]).c_str ()
: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
- if (dd.expect (as<string> (*rs["bin.ar.checksum"])) != nullptr)
+ if (dd.expect (cast<string> (*rs["bin.ar.checksum"])) != nullptr)
l4 ([&]{trace << "ar mismatch forcing update of " << t;});
if (dd.expect (rl) != nullptr)
@@ -804,7 +804,7 @@ namespace build2
}
else
{
- if (dd.expect (as<string> (*rs["cxx.checksum"])) != nullptr)
+ if (dd.expect (cast<string> (*rs["cxx.checksum"])) != nullptr)
l4 ([&]{trace << "compiler mismatch forcing update of " << t;});
}
@@ -877,7 +877,7 @@ namespace build2
// precedence.
//
if (auto l = t["bin.rpath"])
- for (const string& p: as<strings> (*l))
+ for (const string& p: cast<strings> (*l))
sargs.push_back ("-Wl,-rpath," + p);
// Then the paths of the shared libraries we are linking to. Unless
@@ -978,12 +978,12 @@ namespace build2
if (lt == type::a)
{
- args[0] = as<string> (*rs["config.bin.ar"]).c_str ();
+ args[0] = cast<string> (*rs["config.bin.ar"]).c_str ();
args.push_back (relt.string ().c_str ());
}
else
{
- args[0] = as<string> (*rs["config.cxx"]).c_str ();
+ args[0] = cast<string> (*rs["config.cxx"]).c_str ();
args.push_back ("-o");
args.push_back (relt.string ().c_str ());
}
@@ -1048,7 +1048,7 @@ namespace build2
if (ranlib)
{
const char* args[] = {
- as<string> (*ranlib).c_str (), relt.string ().c_str (), nullptr};
+ cast<string> (*ranlib).c_str (), relt.string ().c_str (), nullptr};
if (verb >= 2)
print_process (args);
diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx
index 7c6a4dd..d491079 100644
--- a/build2/cxx/module.cxx
+++ b/build2/cxx/module.cxx
@@ -46,7 +46,7 @@ namespace build2
{
auto l (b["bin.loaded"]);
- if (!l || !as<bool> (*l))
+ if (!l || !cast<bool> (*l))
load_module (false, "bin", r, b, loc);
}
@@ -157,16 +157,16 @@ namespace build2
// cxx.coptions += <overriding options> # Note: '+='.
//
if (const value& v = config::optional (r, "config.cxx.poptions"))
- b.assign ("cxx.poptions") += as<strings> (v);
+ b.assign ("cxx.poptions") += cast<strings> (v);
if (const value& v = config::optional (r, "config.cxx.coptions"))
- b.assign ("cxx.coptions") += as<strings> (v);
+ b.assign ("cxx.coptions") += cast<strings> (v);
if (const value& v = config::optional (r, "config.cxx.loptions"))
- b.assign ("cxx.loptions") += as<strings> (v);
+ b.assign ("cxx.loptions") += cast<strings> (v);
if (const value& v = config::optional (r, "config.cxx.libs"))
- b.assign ("cxx.libs") += as<strings> (v);
+ b.assign ("cxx.libs") += cast<strings> (v);
// config.cxx
//
@@ -176,7 +176,7 @@ namespace build2
// Figure out which compiler we are dealing with, its target, etc.
//
- const path& cxx (path (as<string> (p.first))); // @@ VAR
+ const path& cxx (path (cast<string> (p.first))); // @@ VAR
compiler_info ci (guess (cxx, r["cxx.coptions"]));
// If this is a new value (e.g., we are configuring), then print the
diff --git a/build2/cxx/utility.txx b/build2/cxx/utility.txx
index ed35fdb..c20e437 100644
--- a/build2/cxx/utility.txx
+++ b/build2/cxx/utility.txx
@@ -14,7 +14,7 @@ namespace build2
{
if (auto l = t["cxx.std"])
{
- const string& v (as<string> (*l));
+ const string& v (cast<string> (*l));
// Translate 11 to 0x and 14 to 1y for compatibility with older
// versions of the compiler.
diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx
index d1c2266..4489485 100644
--- a/build2/dist/operation.cxx
+++ b/build2/dist/operation.cxx
@@ -90,7 +90,7 @@ namespace build2
fail << "unknown root distribution directory" <<
info << "did you forget to specify config.dist.root?";
- const dir_path& dist_root (as<dir_path> (*l));
+ const dir_path& dist_root (cast<dir_path> (*l));
if (!dir_exists (dist_root))
fail << "root distribution directory " << dist_root
@@ -102,8 +102,8 @@ namespace build2
fail << "unknown distribution package name" <<
info << "did you forget to set dist.package?";
- const string& dist_package (as<string> (*l));
- const string& dist_cmd (as<string> (*rs->vars["dist.cmd"]));
+ const string& dist_package (cast<string> (*l));
+ const string& dist_cmd (cast<string> (*rs->vars["dist.cmd"]));
// Get the list of operations supported by this project. Skip
// default_id.
@@ -171,7 +171,7 @@ namespace build2
//
if (auto l = rs->vars["subprojects"])
{
- for (auto p: as<subprojects> (*l))
+ for (auto p: cast<subprojects> (*l))
{
const dir_path& pd (p.second);
dir_path out_nroot (out_root / pd);
@@ -209,7 +209,7 @@ namespace build2
//
auto l ((*ft)[dist_var]);
- if (l && !as<bool> (*l))
+ if (l && !cast<bool> (*l))
l5 ([&]{trace << "excluding " << *ft;});
else
files.push_back (ft);
@@ -223,7 +223,7 @@ namespace build2
//
auto l ((*ft)[dist_var]);
- if (l && as<bool> (*l))
+ if (l && cast<bool> (*l))
{
l5 ([&]{trace << "including " << *ft;});
files.push_back (ft);
@@ -295,7 +295,7 @@ namespace build2
//
if (auto l = rs->vars["dist.archives"])
{
- for (const string& e: as<strings> (*l))
+ for (const string& e: cast<strings> (*l))
archive (dist_root, dist_package, e);
}
}
diff --git a/build2/dump.cxx b/build2/dump.cxx
index 13accf7..7e55b1e 100644
--- a/build2/dump.cxx
+++ b/build2/dump.cxx
@@ -22,7 +22,10 @@ namespace build2
if (val.null ())
os << "[null]";
else
- os << val.data_;
+ {
+ names storage;
+ os << reverse (val, storage);
+ }
}
static void
diff --git a/build2/file b/build2/file
index 4f8c8db..b198557 100644
--- a/build2/file
+++ b/build2/file
@@ -93,6 +93,16 @@ namespace build2
bool
bootstrap_src (scope& root);
+ // Return true if this scope has already been bootstrapped, that is, the
+ // following calls have already been made:
+ //
+ // bootstrap_out()
+ // setup_root()
+ // bootstrap_src()
+ //
+ bool
+ bootstrapped (scope& root);
+
// Create and bootstrap outer root scopes, if any. Loading is
// done by load_root_pre() below.
//
diff --git a/build2/file.cxx b/build2/file.cxx
index 09332f1..30ebed4 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -154,7 +154,7 @@ namespace build2
v = out_root;
else
{
- const dir_path& p (as<dir_path> (v));
+ const dir_path& p (cast<dir_path> (v));
if (p != out_root)
fail << "new out_root " << out_root << " does not match "
@@ -170,7 +170,7 @@ namespace build2
v = src_root;
else
{
- const dir_path& p (as<dir_path> (v));
+ const dir_path& p (cast<dir_path> (v));
if (p != src_root)
fail << "new src_root " << src_root << " does not match "
@@ -190,7 +190,8 @@ namespace build2
// Register and set src_path.
//
if (s.src_path_ == nullptr)
- s.src_path_ = &scopes.insert (as<dir_path> (v), &s, false, true)->first;
+ s.src_path_ = &scopes.insert (
+ cast<dir_path> (v), &s, false, true)->first;
}
scope&
@@ -227,7 +228,7 @@ namespace build2
if (!v)
v = out_base;
else
- assert (as<dir_path> (v) == out_base);
+ assert (cast<dir_path> (v) == out_base);
}
{
@@ -236,7 +237,7 @@ namespace build2
if (!v)
v = src_base;
else
- assert (as<dir_path> (v) == src_base);
+ assert (cast<dir_path> (v) == src_base);
}
return s;
@@ -330,7 +331,7 @@ namespace build2
else
{
src_root_v = extract_variable (f, "src_root");
- src_root = &as<dir_path> (src_root_v);
+ src_root = &cast<dir_path> (src_root_v);
l5 ([&]{trace << "extracted src_root " << *src_root << " for "
<< out_root;});
}
@@ -339,7 +340,7 @@ namespace build2
string name;
{
value v (extract_variable (*src_root / bootstrap_file, "project"));
- name = move (as<string> (v));
+ name = cast<string> (move (v));
}
l5 ([&]{trace << "extracted project name '" << name << "' for "
@@ -486,7 +487,7 @@ namespace build2
}
else
{
- const dir_path& vd (as<dir_path> (v));
+ const dir_path& vd (cast<dir_path> (v));
if (vd != rd)
{
@@ -523,16 +524,15 @@ namespace build2
}
// See if we have any subprojects. In a sense, this is the other
- // side/direction of the amalgamation logic above. Here, the
- // subprojects variable may or may not be set by the user (in
- // bootstrap.build) or by an earlier call to this function for
- // the same scope. When set by the user, the empty special value
- // means that there are no subproject and none should be searched
- // for (and which we convert to NULL below). Otherwise, it is a
- // list of directory[=project] pairs. The directory must be
- // relative to our out_root. If the project name is not specified,
- // then we have to figure it out. When subprojects are calculated,
- // the NULL value indicates that we found no subprojects.
+ // side/direction of the amalgamation logic above. Here, the subprojects
+ // variable may or may not be set by the user (in bootstrap.build) or by
+ // an earlier call to this function for the same scope. When set by the
+ // user, the empty special value means that there are no subproject and
+ // none should be searched for (and which we convert to NULL below).
+ // Otherwise, it is a list of [project@]directory pairs. The directory
+ // must be relative to our out_root. If the project name is not specified,
+ // then we have to figure it out. When subprojects are calculated, the
+ // NULL value indicates that we found no subprojects.
//
{
const variable& var (var_pool.find ("subprojects"));
@@ -567,53 +567,73 @@ namespace build2
v = nullptr;
else
{
- // Pre-scan the value and convert it to the "canonical" form,
+ // Scan the (untyped) value and convert it to the "canonical" form,
// that is, a list of name@dir pairs.
//
- for (auto i (v.data_.begin ()); i != v.data_.end (); ++i)
+ subprojects sps;
+ names& ns (cast<names> (v));
+
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
{
+ // Project name.
+ //
+ string n;
if (i->pair)
{
- // Project name.
- //
- if (!assign<string> (*i) || as<string> (*i).empty ())
+ try
+ {
+ n = convert<string> (move (*i));
+
+ if (n.empty ())
+ fail << "empty project name in variable subprojects";
+ }
+ catch (const invalid_argument&)
+ {
fail << "expected project name instead of '" << *i << "' in "
- << "the subprojects variable";
+ << "variable subprojects";
+ }
++i; // Got to have the second half of the pair.
}
- if (!assign<dir_path> (*i))
- fail << "expected directory instead of '" << *i << "' in the "
- << "subprojects variable";
+ // Directory.
+ //
+ dir_path d;
+ try
+ {
+ d = convert<dir_path> (move (*i));
- auto& d (as<dir_path> (*i));
+ if (d.empty ())
+ fail << "empty directory in variable subprojects";
+ }
+ catch (const invalid_argument&)
+ {
+ fail << "expected directory instead of '" << *i << "' in "
+ << "variable subprojects";
+ }
// Figure out the project name if the user didn't specify one.
//
- if (!i->pair)
+ if (n.empty ())
{
// Pass fallback src_root since this is a subproject that
// was specified by the user so it is most likely in our
// src.
//
- string n (find_project_name (out_root / d, src_root / d));
+ n = find_project_name (out_root / d, src_root / d);
// See find_subprojects() for details on unnamed projects.
//
if (n.empty ())
n = d.posix_string () + '/';
-
- i = v.data_.emplace (i, move (n));
-
- i->pair = true;
- ++i;
}
+
+ sps.emplace (move (n), move (d));
}
- // Make it of the map type.
+ // Change the value to the typed map.
//
- assign<subprojects> (v, var);
+ v = move (sps);
}
}
}
@@ -621,6 +641,17 @@ namespace build2
return r;
}
+ bool
+ bootstrapped (scope& root)
+ {
+ // Use the subprojects variable set by bootstrap_src() as an indicator.
+ // It should either be NULL or typed (so we assume that the user will
+ // never set it to NULL).
+ //
+ auto l (root.vars["subprojects"]);
+ return l.defined () && (l->null () || l->type != nullptr);
+ }
+
void
create_bootstrap_outer (scope& root)
{
@@ -629,7 +660,7 @@ namespace build2
if (!l)
return;
- const dir_path& d (as<dir_path> (*l));
+ const dir_path& d (cast<dir_path> (*l));
dir_path out_root (root.out_path () / d);
out_root.normalize ();
@@ -645,25 +676,29 @@ namespace build2
// by #1 seems reasonable.
//
scope& rs (create_root (out_root, dir_path ()));
- bootstrap_out (rs); // #3 happens here, if at all.
-
- value& v (rs.assign ("src_root"));
- if (!v)
+ if (!bootstrapped (rs))
{
- if (is_src_root (out_root)) // #2
- v = out_root;
- else // #1
+ bootstrap_out (rs); // #3 happens here, if at all.
+
+ value& v (rs.assign ("src_root"));
+
+ if (!v)
{
- dir_path src_root (root.src_path () / d);
- src_root.normalize ();
- v = move (src_root);
+ if (is_src_root (out_root)) // #2
+ v = out_root;
+ else // #1
+ {
+ dir_path src_root (root.src_path () / d);
+ src_root.normalize ();
+ v = move (src_root);
+ }
}
- }
- setup_root (rs);
+ setup_root (rs);
+ bootstrap_src (rs);
+ }
- bootstrap_src (rs);
create_bootstrap_outer (rs);
// Check if we are strongly amalgamated by this outer root scope.
@@ -677,12 +712,9 @@ namespace build2
{
if (auto l = root.vars["subprojects"])
{
- for (const name& n: *l)
+ for (const auto& p: cast<subprojects> (*l))
{
- if (n.pair)
- continue; // Skip project names.
-
- dir_path out_root (root.out_path () / n.dir);
+ dir_path out_root (root.out_path () / p.second);
if (!out_base.sub (out_root))
continue;
@@ -690,18 +722,21 @@ namespace build2
// The same logic to src_root as in create_bootstrap_outer().
//
scope& rs (create_root (out_root, dir_path ()));
- bootstrap_out (rs);
- value& v (rs.assign ("src_root"));
+ if (!bootstrapped (rs))
+ {
+ bootstrap_out (rs);
- if (!v)
- v = is_src_root (out_root)
- ? out_root
- : (root.src_path () / n.dir);
+ value& v (rs.assign ("src_root"));
- setup_root (rs);
+ if (!v)
+ v = is_src_root (out_root)
+ ? out_root
+ : (root.src_path () / p.second);
- bootstrap_src (rs);
+ setup_root (rs);
+ bootstrap_src (rs);
+ }
// Check if we strongly amalgamated this inner root scope.
//
@@ -784,7 +819,7 @@ namespace build2
{
// First check the amalgamation itself.
//
- if (r != &iroot && as<string> (*r->vars["project"]) == project)
+ if (r != &iroot && cast<string> (*r->vars["project"]) == project)
{
out_root = r->out_path ();
break;
@@ -792,7 +827,7 @@ namespace build2
if (auto l = r->vars["subprojects"])
{
- const auto& m (as<subprojects> (*l));
+ const auto& m (cast<subprojects> (*l));
auto i (m.find (project));
if (i != m.end ())
@@ -812,11 +847,11 @@ namespace build2
if (out_root.empty ())
{
const variable& var (
- var_pool.find ("config.import." + project, dir_path_type));
+ var_pool.find<dir_path> ("config.import." + project));
if (auto l = iroot[var])
{
- out_root = as<dir_path> (*l);
+ out_root = cast<dir_path> (*l);
if (l.belongs (*global_scope)) // A value from command line.
{
@@ -837,7 +872,7 @@ namespace build2
//
// @@ CMDVAR
//
- dir_path& d (as<dir_path> (const_cast<value&> (*l)));
+ dir_path& d (cast<dir_path> (const_cast<value&> (*l)));
if (d != out_root)
d = out_root;
}
@@ -868,34 +903,37 @@ namespace build2
src_root = is_src_root (out_root) ? out_root : dir_path ();
root = &create_root (out_root, src_root);
- bootstrap_out (*root);
-
- // Check that the bootstrap process set src_root.
- //
- if (auto l = root->vars["src_root"])
+ if (!bootstrapped (*root))
{
- const dir_path& p (as<dir_path> (*l));
+ bootstrap_out (*root);
- if (!src_root.empty () && p != src_root)
- fail (loc) << "bootstrapped src_root " << p << " does not match "
- << "discovered " << src_root;
- }
- else
- fail (loc) << "unable to determine src_root for imported "
- << project <<
- info << "consider configuring " << out_root;
+ // Check that the bootstrap process set src_root.
+ //
+ if (auto l = root->vars["src_root"])
+ {
+ const dir_path& p (cast<dir_path> (*l));
- setup_root (*root);
- bootstrap_src (*root);
+ if (!src_root.empty () && p != src_root)
+ fail (loc) << "bootstrapped src_root " << p << " does not match "
+ << "discovered " << src_root;
+ }
+ else
+ fail (loc) << "unable to determine src_root for imported "
+ << project <<
+ info << "consider configuring " << out_root;
+
+ setup_root (*root);
+ bootstrap_src (*root);
+ }
// Now we know this project's name as well as all its subprojects.
//
- if (as<string> (*root->vars["project"]) == project)
+ if (cast<string> (*root->vars["project"]) == project)
break;
if (auto l = root->vars["subprojects"])
{
- const auto& m (as<subprojects> (*l));
+ const auto& m (cast<subprojects> (*l));
auto i (m.find (project));
if (i != m.end ())
diff --git a/build2/install/module.cxx b/build2/install/module.cxx
index 579d454..8e3d6fd 100644
--- a/build2/install/module.cxx
+++ b/build2/install/module.cxx
@@ -154,7 +154,7 @@ namespace build2
if (first)
{
bool s (config::specified (r, "config.install"));
- const string& n (as<string> (*r["project"]));
+ const string& n (cast<string> (*r["project"]));
set_dir (s, r, "root", "", "", "755", "install");
set_dir (s, r, "data_root", "root", "644");
diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx
index 6933828..89463f8 100644
--- a/build2/install/rule.cxx
+++ b/build2/install/rule.cxx
@@ -31,7 +31,7 @@ namespace build2
if (!l)
return nullptr;
- const dir_path& r (as<dir_path> (*l));
+ const dir_path& r (cast<dir_path> (*l));
return r.simple () && r.string () == "false" ? nullptr : &r;
}
@@ -64,7 +64,7 @@ namespace build2
//
auto l (pt["install"]);
- if (l && as<dir_path> (*l).string () == "false")
+ if (l && cast<dir_path> (*l).string () == "false")
{
l5 ([&]{trace << "ignoring " << pt;});
continue;
@@ -148,7 +148,7 @@ namespace build2
// See if the user instructed us not to install it.
//
auto l ((*pt)["install"]);
- if (l && as<dir_path> (*l).string () == "false")
+ if (l && cast<dir_path> (*l).string () == "false")
continue;
build2::match (a, *pt);
@@ -216,10 +216,13 @@ namespace build2
struct install_dir
{
+ // @@ Why do we copy these? Why not just point to the values in vars?
+ //
+
dir_path dir;
string sudo;
string cmd; //@@ VAR type
- const_strings_value options {nullptr};
+ strings options;
string mode;
string dir_mode;
};
@@ -239,7 +242,7 @@ namespace build2
args.push_back (base.cmd.c_str ());
args.push_back ("-d");
- if (base.options.d != nullptr) //@@ VAR
+ if (!base.options.empty ())
append_options (args, base.options);
args.push_back ("-m");
@@ -285,7 +288,7 @@ namespace build2
args.push_back (base.cmd.c_str ());
- if (base.options.d != nullptr) //@@ VAR
+ if (!base.options.empty ())
append_options (args, base.options);
args.push_back ("-m");
@@ -355,11 +358,11 @@ namespace build2
//
if (var != nullptr)
{
- if (auto l = s[*var + ".sudo"]) r.sudo = as<string> (*l);
- if (auto l = s[*var + ".cmd"]) r.cmd = as<string> (*l);
- if (auto l = s[*var + ".mode"]) r.mode = as<string> (*l);
- if (auto l = s[*var + ".dir_mode"]) r.dir_mode = as<string> (*l);
- if (auto l = s[*var + ".options"]) r.options = as<strings> (*l);
+ if (auto l = s[*var + ".sudo"]) r.sudo = cast<string> (*l);
+ if (auto l = s[*var + ".cmd"]) r.cmd = cast<string> (*l);
+ if (auto l = s[*var + ".mode"]) r.mode = cast<string> (*l);
+ if (auto l = s[*var + ".dir_mode"]) r.dir_mode = cast<string> (*l);
+ if (auto l = s[*var + ".options"]) r.options = cast<strings> (*l);
}
// Set defaults for unspecified components.
@@ -400,12 +403,12 @@ namespace build2
//
install_dir d (
resolve (t.base_scope (),
- as<dir_path> (*t["install"]))); // We know it's there.
+ cast<dir_path> (*t["install"]))); // We know it's there.
// Override mode if one was specified.
//
if (auto l = t["install.mode"])
- d.mode = as<string> (*l);
+ d.mode = cast<string> (*l);
install (d, ft);
return (r |= target_state::changed);
diff --git a/build2/module.cxx b/build2/module.cxx
index e05e22c..b090173 100644
--- a/build2/module.cxx
+++ b/build2/module.cxx
@@ -98,12 +98,10 @@ namespace build2
bool l (i != lm.end ());
bool c (l && i->second.init (rs, bs, loc, i->second.module, f, opt));
- const variable& lv (var_pool.find (name + ".loaded",
- variable_visibility::project,
- bool_type));
- const variable& cv (var_pool.find (name + ".configured",
- variable_visibility::project,
- bool_type));
+ const variable& lv (var_pool.find<bool> (name + ".loaded",
+ variable_visibility::project));
+ const variable& cv (var_pool.find<bool> (name + ".configured",
+ variable_visibility::project));
bs.assign (lv) = l;
bs.assign (cv) = c;
diff --git a/build2/name b/build2/name
index a4de95c..d0a115f 100644
--- a/build2/name
+++ b/build2/name
@@ -2,21 +2,19 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+// Note: include <build2/types> instead of this file directly.
+//
+
#ifndef BUILD2_NAME
#define BUILD2_NAME
-#include <string>
-#include <vector>
-#include <iosfwd>
+// We cannot include <build2/utility> since it includes <build2/types>.
+//
#include <utility> // move()
-#include <butl/path>
-
-// Note: include <build2/types> instead of this file directly.
-//
namespace build2
{
- using butl::dir_path;
+ using std::move;
// A name is what we operate on by default. Depending on the context,
// it can be interpreted as a target or prerequisite name. A name
@@ -31,27 +29,29 @@ namespace build2
//
struct name
{
+ const string* proj = nullptr; // Points to project_name_pool.
+ dir_path dir;
+ string type;
+ string value;
+ bool pair = false; // True if first half of a pair.
+
name () = default;
- explicit name (std::string v): value (std::move (v)) {}
- name& operator= (std::string v) {return *this = name (std::move (v));}
+ explicit name (string v): value (move (v)) {}
+ name& operator= (string v) {return *this = name (move (v));}
- explicit name (dir_path d): dir (std::move (d)) {}
- name& operator= (dir_path d) {return *this = name (std::move (d));}
+ explicit name (dir_path d): dir (move (d)) {}
+ name& operator= (dir_path d) {return *this = name (move (d));}
- name (std::string t, std::string v)
- : type (std::move (t)), value (std::move (v)) {}
+ name (string t, string v): type (move (t)), value (move (v)) {}
- name (dir_path d, std::string t, std::string v)
- : dir (std::move (d)), type (std::move (t)), value (std::move (v)) {}
+ name (dir_path d, string t, string v)
+ : dir (move (d)), type (move (t)), value (move (v)) {}
// The first argument should be from project_name_pool.
//
- name (const std::string* p, dir_path d, std::string t, std::string v)
- : proj (p),
- dir (std::move (d)),
- type (std::move (t)),
- value (std::move (v)) {}
+ name (const string* p, dir_path d, string t, string v)
+ : proj (p), dir (move (d)), type (move (t)), value (move (v)) {}
bool
qualified () const {return proj != nullptr;}
@@ -87,32 +87,39 @@ namespace build2
untyped () && !dir.empty () && value.empty ();
}
- const std::string* proj = nullptr; // Points to project_name_pool.
- dir_path dir;
- std::string type;
- std::string value;
- bool pair = false; // True if first half of a pair.
+ int
+ compare (const name&) const;
+
+ // The result is an unqualified, simple empty name.
+ //
+ void
+ clear ();
};
inline bool
- operator== (const name& x, const name& y)
- {
- return x.proj == y.proj && // Pooled, so can just compare pointers.
- x.type == y.type &&
- x.dir == y.dir &&
- x.value == y.value;
- }
+ operator== (const name& x, const name& y) {return x.compare (y) == 0;}
inline bool
operator!= (const name& x, const name& y) {return !(x == y);}
- typedef std::vector<name> names;
+ inline bool
+ operator< (const name& x, const name& y) {return x.compare (y) < 0;}
+
+ ostream&
+ operator<< (ostream&, const name&);
- std::ostream&
- operator<< (std::ostream&, const name&);
+ // Vector of names.
+ //
+ using names = vector<name>;
+ using names_view = vector_view<const name>;
- std::ostream&
- operator<< (std::ostream&, const names&);
+ ostream&
+ operator<< (ostream&, const names_view&);
+
+ inline ostream&
+ operator<< (ostream& os, const names& n) {return os << names_view (n);}
}
+#include <build2/name.ixx>
+
#endif // BUILD2_NAME
diff --git a/build2/name.cxx b/build2/name.cxx
index 0d7211c..296b4ec 100644
--- a/build2/name.cxx
+++ b/build2/name.cxx
@@ -2,12 +2,10 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#include <build2/name>
+#include <build2/types> // Note: not <build2/names>
#include <build2/diagnostics>
-using namespace std;
-
namespace build2
{
ostream&
@@ -42,7 +40,7 @@ namespace build2
}
ostream&
- operator<< (ostream& os, const names& ns)
+ operator<< (ostream& os, const names_view& ns)
{
for (auto i (ns.begin ()), e (ns.end ()); i != e; )
{
diff --git a/build2/name.ixx b/build2/name.ixx
new file mode 100644
index 0000000..21bd125
--- /dev/null
+++ b/build2/name.ixx
@@ -0,0 +1,42 @@
+// file : build2/name.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+namespace build2
+{
+ inline int name::
+ compare (const name& x) const
+ {
+ int r;
+
+ // Project string is pooled, so for equality can just compare pointers.
+ //
+ r = proj == x.proj
+ ? 0
+ : proj == nullptr || (x.proj != nullptr && *proj < *x.proj) ? -1 : 1;
+
+ if (r == 0)
+ r = dir.compare (x.dir);
+
+ if (r == 0)
+ r = type.compare (x.type);
+
+ if (r == 0)
+ r = value.compare (x.value);
+
+ if (r == 0)
+ r = pair < x.pair ? -1 : (pair > x.pair ? 1 : 0);
+
+ return r;
+ }
+
+ inline void name::
+ clear ()
+ {
+ proj = nullptr;
+ dir.clear ();
+ type.clear ();
+ value.clear ();
+ pair = false;
+ }
+}
diff --git a/build2/parser.cxx b/build2/parser.cxx
index ab9012c..b19fcb3 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -1062,14 +1062,21 @@ namespace build2
const location nsl (get_location (t, &path_));
names_type ns (names (t, tt));
- // Should evaluate to true or false.
+ // Should evaluate to 'true' or 'false'.
//
- if (ns.size () != 1 || !assign<bool> (ns[0]))
+ try
+ {
+ if (ns.size () != 1)
+ throw invalid_argument (string ());
+
+ bool e (convert<bool> (move (ns[0])));
+ take = (k.back () == '!' ? !e : e);
+ }
+ catch (const invalid_argument&)
+ {
fail (nsl) << "expected " << k << "-expression to evaluate to "
<< "'true' or 'false' instead of '" << ns << "'";
-
- bool e (ns[0].value == "true");
- take = (k.back () == '!' ? !e : e);
+ }
}
}
else
@@ -1574,8 +1581,8 @@ namespace build2
// pretty quickly end up with a list of names that we need
// to splice into the result.
//
- names_type lv_data;
- const names_type* plv;
+ names_type lv_storage;
+ names_view lv;
location loc;
const char* what; // Variable or evaluation context.
@@ -1630,10 +1637,10 @@ namespace build2
tt = peek ();
- if (lv_data.empty ())
+ if (lv_storage.empty ())
continue;
- plv = &lv_data;
+ lv = lv_storage;
what = "function call";
}
else
@@ -1660,27 +1667,26 @@ namespace build2
if (!l || l->empty ())
continue;
- plv = &l->data_;
+ lv = reverse (*l, lv_storage);
what = "variable expansion";
}
}
else
{
loc = get_location (t, &path_);
- lv_data = eval (t, tt);
+ lv_storage = eval (t, tt);
tt = peek ();
- if (lv_data.empty ())
+ if (lv_storage.empty ())
continue;
- plv = &lv_data;
+ lv = lv_storage;
what = "context evaluation";
}
- // @@ Could move if (lv == &lv_data).
+ // @@ Could move if lv is lv_storage.
//
- const names_type& lv (*plv);
// Should we accumulate? If the buffer is not empty, then
// we continue accumulating (the case where we are separated
diff --git a/build2/scope b/build2/scope
index e92361d..9878e7f 100644
--- a/build2/scope
+++ b/build2/scope
@@ -141,10 +141,7 @@ namespace build2
assign (const variable& var) {return vars.assign (var).first.get ();}
value&
- assign (const string& name, const build2::value_type* type = nullptr)
- {
- return vars.assign (name, type).first.get ();
- }
+ assign (const string& name) {return vars.assign (name).first.get ();}
template <typename T>
value&
diff --git a/build2/target b/build2/target
index cdf6037..4c5d046 100644
--- a/build2/target
+++ b/build2/target
@@ -292,10 +292,7 @@ namespace build2
assign (const variable& var) {return vars.assign (var).first;}
value&
- assign (const string& name, const build2::value_type* type = nullptr)
- {
- return vars.assign (name, type).first;
- }
+ assign (const string& name) {return vars.assign (name).first;}
template <typename T>
value&
diff --git a/build2/target.txx b/build2/target.txx
index 4999f61..2cb4d2e 100644
--- a/build2/target.txx
+++ b/build2/target.txx
@@ -26,7 +26,7 @@ namespace build2
{
// Help the user here and strip leading '.' from the extension.
//
- const string& e (as<string> (*l));
+ const string& e (cast<string> (*l));
return &extension_pool.find (
!e.empty () && e.front () == '.' ? string (e, 1) : e);
}
diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx
index efe265b..0b3f59f 100644
--- a/build2/test/rule.cxx
+++ b/build2/test/rule.cxx
@@ -70,7 +70,7 @@ namespace build2
l = t.base_scope ()[
var_pool.find<bool> (string("test.") + t.type ().name)];
- r = l && as<bool> (*l);
+ r = l && cast<bool> (*l);
}
// If this is the update pre-operation, then all we really need to
@@ -181,12 +181,12 @@ namespace build2
fail << "both test.roundtrip and test.input/output specified "
<< "for target " << t;
- in = on = &as<name> (*rl);
+ in = on = &cast<name> (*rl);
}
else
{
- in = il ? &as<name> (*il) : nullptr;
- on = ol ? &as<name> (*ol) : nullptr;
+ in = il ? &cast<name> (*il) : nullptr;
+ on = ol ? &cast<name> (*ol) : nullptr;
}
// Resolve them to targets, which normally would be existing files
@@ -296,7 +296,7 @@ namespace build2
}
if (l)
- append_options (args, as<strings> (*l));
+ append_options (args, cast<strings> (*l));
}
// The format of args shall be:
diff --git a/build2/types b/build2/types
index 54056ba..166f47a 100644
--- a/build2/types
+++ b/build2/types
@@ -25,8 +25,7 @@
#include <butl/fdstream>
#include <butl/optional>
#include <butl/timestamp>
-
-#include <build2/name>
+#include <butl/vector-view>
namespace build2
{
@@ -50,6 +49,7 @@ namespace build2
using std::weak_ptr;
using std::vector;
+ using butl::vector_view; // <butl/vector-view>
using strings = vector<string>;
using cstrings = vector<const char*>;
@@ -109,9 +109,10 @@ namespace build2
using butl::ifdstream;
using butl::ofdstream;
-
- // <build2/name>
- //
}
+// <build2/name>
+//
+#include <build2/name>
+
#endif // BUILD2_TYPES
diff --git a/build2/utility b/build2/utility
index a3f9be4..e906b55 100644
--- a/build2/utility
+++ b/build2/utility
@@ -150,8 +150,6 @@ namespace build2
//
class value;
template <typename> struct lookup;
- template <typename, typename> struct vector_value;
- using const_strings_value = vector_value<string, const names>;
void
append_options (cstrings&, const lookup<const value>&);
@@ -160,10 +158,10 @@ namespace build2
hash_options (sha256&, const lookup<const value>&);
void
- append_options (cstrings&, const const_strings_value&);
+ append_options (cstrings&, const strings&);
void
- hash_options (sha256&, const const_strings_value&);
+ hash_options (sha256&, const strings&);
// Check if a specified option is present in the variable value.
// T is either target or scope.
diff --git a/build2/utility.cxx b/build2/utility.cxx
index 4c1acd3..29e8b1e 100644
--- a/build2/utility.cxx
+++ b/build2/utility.cxx
@@ -147,18 +147,18 @@ namespace build2
append_options (cstrings& args, const lookup<const value>& l)
{
if (l)
- append_options (args, as<strings> (*l));
+ append_options (args, cast<strings> (*l));
}
void
hash_options (sha256& csum, const lookup<const value>& l)
{
if (l)
- hash_options (csum, as<strings> (*l));
+ hash_options (csum, cast<strings> (*l));
}
void
- append_options (cstrings& args, const const_strings_value& sv)
+ append_options (cstrings& args, const strings& sv)
{
if (!sv.empty ())
{
@@ -170,7 +170,7 @@ namespace build2
}
void
- hash_options (sha256& csum, const const_strings_value& sv)
+ hash_options (sha256& csum, const strings& sv)
{
for (const string& s: sv)
csum.append (s);
@@ -181,7 +181,7 @@ namespace build2
{
if (l)
{
- for (const string& s: as<strings> (*l))
+ for (const string& s: cast<strings> (*l))
{
if (s == option)
return true;
diff --git a/build2/utility.ixx b/build2/utility.ixx
index 596eb78..d42d6fd 100644
--- a/build2/utility.ixx
+++ b/build2/utility.ixx
@@ -44,6 +44,7 @@ namespace build2
inline void
hash_options (sha256& csum, T& s, const char* var)
{
+
hash_options (csum, s[var]);
}
diff --git a/build2/variable b/build2/variable
index 029aafa..b5a32d8 100644
--- a/build2/variable
+++ b/build2/variable
@@ -6,9 +6,8 @@
#define BUILD2_VARIABLE
#include <map>
-#include <iterator> // tags, etc.
#include <functional> // hash
-#include <type_traits> // conditional, is_reference, remove_reference, etc.
+#include <type_traits> // aligned_storage
#include <unordered_set>
#include <butl/prefix-map>
@@ -20,18 +19,50 @@
namespace build2
{
+ class value;
struct variable;
- // If assign is NULL, then the value is assigned as is. If append
- // is NULL, then the names are appended to the end of the value
- // and assign is called, if not NULL. Variable is provided primarily
- // for diagnostics. Return true if the resulting value is not empty.
- //
struct value_type
{
- const char* name;
- bool (*const assign) (names&, const variable&);
- bool (*const append) (names&, names, const variable&);
+ const char* name; // Type name for diagnostics.
+ const size_t size; // Type size in value::data_ (only used for PODs).
+
+ // Destroy the value. If it is NULL, then the type is assumed to be POD
+ // with a trivial destructor.
+ //
+ void (*const dtor) (value&);
+
+ // Copy/move constructor and copy/move assignment for data_. If NULL, then
+ // assume the stored data is POD. If move is true then the second argument
+ // can be const_cast and moved from. copy_assign() is only called with
+ // non-NULL first argument.
+ //
+ void (*const copy_ctor) (value&, const value&, bool move);
+ void (*const copy_assign) (value&, const value&, bool move);
+
+ // While assign cannot be NULL, if append or prepend is NULL, then this
+ // means this type doesn't support this operation. Variable is provided
+ // primarily for diagnostics. Return true if the resulting value is not
+ // empty.
+ //
+ bool (*const assign) (value&, names&&, const variable&);
+ bool (*const append) (value&, names&&, const variable&);
+ bool (*const prepend) (value&, names&&, const variable&);
+
+ // Reverse the value back to a vector of names. Storage can be used by the
+ // implementation if necessary. Cannot be NULL.
+ //
+ names_view (*const reverse) (const value&, names& storage);
+
+ // Cast value::data_ storage to value type so that the result can be
+ // static_cast to const T*. If it is NULL, then cast data_ directly. Note
+ // that this function is used for both const and non-const values.
+ //
+ const void* (*const cast) (const value&);
+
+ // If NULL, then the types are compared as PODs using memcmp().
+ //
+ int (*const compare) (const value&, const value&);
};
enum class variable_visibility
@@ -59,117 +90,127 @@ namespace build2
// value
//
+ enum class value_state {null, empty, filled};
+
class value
{
public:
- // By default we create NULL value.
- //
- explicit value (const value_type* t = nullptr)
- : type (t), state_ (state_type::null) {}
-
- value (value&&) = default;
-
- value&
- operator= (nullptr_t)
- {
- data_.clear ();
- state_ = state_type::null;
- return *this;
- }
+ const value_type* type; // NULL means this value is not (yet) typed.
+ value_state state;
- value&
- operator= (value&&);
+ bool null () const {return state == value_state::null;}
+ bool empty () const {return state == value_state::empty;}
- value&
- operator= (const value& v)
- {
- if (&v != this)
- *this = value (v);
- return *this;
- }
+ explicit operator bool () const {return !null ();}
+ bool operator== (nullptr_t) const {return null ();}
+ bool operator!= (nullptr_t) const {return !null ();}
- value&
- operator= (reference_wrapper<value> v) {return *this = v.get ();}
+ // Creation. A newly created value is NULL and can be reset back to NULL
+ // by assigning nullptr. Values can be copied and copy-assigned. Note that
+ // for assignment, the values' types should be the same of LHS should be
+ // untyped.
+ //
+ //
+ public:
+ ~value () {if (!null ()) *this = nullptr;}
- value&
- operator= (reference_wrapper<const value> v) {return *this = v.get ();}
+ explicit
+ value (const value_type* t = nullptr)
+ : type (t), state (value_state::null) {}
value&
- append (value, const variable&); // Aka operator+=().
+ operator= (nullptr_t);
- value&
- prepend (value, const variable&); // Aka operator=+().
+ value (value&&);
+ explicit value (const value&);
+ value& operator= (value&&);
+ value& operator= (const value&);
+ value& operator= (reference_wrapper<value>);
+ value& operator= (reference_wrapper<const value>);
- // Forwarded to the representation type's assign()/append() (see below).
+ // Assign/Append/Prepend.
//
- template <typename T> value& operator= (T);
- value& operator= (const char* v) {return *this = string (v);}
-
- template <typename T> value& operator+= (T);
- value& operator+= (const char* v) {return *this += string (v);}
-
- private:
- explicit value (const value&) = default;
-
public:
- const value_type* type; // NULL means this value is not (yet) typed.
-
- bool null () const {return state_ == state_type::null;}
- bool empty () const {return state_ == state_type::empty;}
-
- explicit operator bool () const {return !null ();}
- bool operator== (nullptr_t) const {return null ();}
- bool operator!= (nullptr_t) const {return !null ();}
-
- // Raw data read interface.
+ // Assign/append a typed value. For assign, LHS should be either of the
+ // same type or untyped. For append, LHS should be either of the same type
+ // or untyped and NULL.
//
- using const_iterator = names::const_iterator;
+ template <typename T> value& operator= (T);
+ template <typename T> value& operator+= (T);
- const_iterator begin () const {return data_.begin ();}
- const_iterator end () const {return data_.end ();}
+ value& operator= (const char* v) {return *this = string (v);}
+ value& operator+= (const char* v) {return *this += string (v);}
- // Raw data write interface. Note that it triggers callbacks for
- // typed values. Variable is passed for diagnostics.
+ // Assign/append/prepend raw data. Variable is normally only used for
+ // diagnostics.
//
void
- assign (names, const variable&);
+ assign (names&&, const variable&);
void
- append (names, const variable&);
+ append (names&&, const variable&);
void
- prepend (names, const variable&);
+ prepend (names&&, const variable&);
+ // Implementation details, don't use directly except in representation
+ // type implementations.
+ //
public:
- // Don't use directly except in the implementation of representation
- // types, in which case take care to update state.
+ // Fast, unchecked cast of data_ to T.
//
- enum class state_type {null, empty, filled} state_;
- names data_;
+ template<typename T> T& as () & {return reinterpret_cast<T&> (data_);}
+ template<typename T> T&& as () && {return reinterpret_cast<T&&> (data_);}
+ template<typename T> const T& as () const& {
+ return reinterpret_cast<const T&> (data_);}
+
+ public:
+ // The maximum size we can store directly in the value is that of a name,
+ // which is sufficient for the most commonly used types (string, vector,
+ // map) on all the platforms that we support (each type should static
+ // assert this in its value_traits specialization below). Types that don't
+ // fit will have to be handled with an extra dynamic allocation.
+ //
+ std::aligned_storage<sizeof (name)>::type data_;
+ static const size_t size_ = sizeof (data_);
+
+ // Make sure we have sufficient storage for untyped values.
+ //
+ static_assert (sizeof (names) <= size_, "insufficient space");
};
- //@@ Right now we assume that for each value type each value has a
- // unique representation. This is currently not the case for map.
+ // The values should be of the same type (or both be untyped). NULL values
+ // compare equal.
//
- inline bool
- operator== (const value& x, const value& y)
- {
- return x.state_ == y.state_ && x.data_ == y.data_;
- }
+ bool operator== (const value&, const value&);
+ bool operator!= (const value&, const value&);
- inline bool
- operator!= (const value& x, const value& y) {return !(x == y);}
+ // Value cast.
+ //
+ // Why are these non-members? The cast is easier on the eyes and is also
+ // consistent with the cast operators. The other two are for symmetry.
+ //
+ template <typename T> T& cast (value&);
+ template <typename T> T&& cast (value&&);
+ template <typename T> const T& cast (const value&);
- // Assign value type to the value. This triggers the assign callback.
+ // Assign value type to the value. Variable is normally only used for
+ // diagnostics.
//
template <typename T>
- void assign (value&, const variable&);
- void assign (value&, const value_type*, const variable&);
+ void typify (value&, const variable&);
+ void typify (value&, const value_type&, const variable&);
+
+ // Reverse the value back to names. The value should no be NULL and storage
+ // should be empty.
+ //
+ names_view
+ reverse (const value&, names& storage);
// lookup
//
- // A variable can be undefined, NULL, or contain a (potentially
- // empty) value.
+ // A variable can be undefined, NULL, or contain a (potentially empty)
+ // value.
//
struct variable_map;
@@ -206,390 +247,207 @@ namespace build2
// Representation types.
//
- template <typename T> struct value_traits;
-
- // Value cast.
+ // Potential optimizations:
//
- template <typename T> typename value_traits<T>::type as (value&);
- template <typename T> typename value_traits<T>::const_type as (const value&);
-
- // Try to "assign" a simple value type to the value stored in name. Return
- // false if the value is not valid for this type. The second version is
- // called for a pair and it is expected to merge the result into the first
- // name.
+ // - Split value::operator=/+=() into const T and T&&, also overload
+ // value_traits functions that they call.
+ //
+ // - Specialization for vector<names> (if used and becomes critical).
+ //
+ //
+ template <typename T>
+ struct value_traits;
+ // {
+ // static_assert (sizeof (T) <= value::size_, "insufficient space");
+ //
+ // // Convert name to T. If rhs is not NULL, then it is the second half
+ // // of a pair. Only needs to be provided by simple types. Throw
+ // // invalid_argument (without a message) if the name is not a valid
+ // // representation of value (in which case the name should remain
+ // // unchanged for diagnostics).
+ // //
+ // static T convert (name&&, name* rhs);
+ //
+ // // Assign/append/prepend T to value and return true if the result is
+ // // not empty. Value is already of type T but can be NULL.
+ // //
+ // static bool assign (value&, T&&);
+ // static bool append (value&, T&&);
+ // static bool prepend (value&, T&&);
+ //
+ // // Reverse a value back to name. Only needs to be provided by simple
+ // // types.
+ // //
+ // static name reverse (const T&);
//
- template <typename T> bool assign (name&);
- template <typename T> bool assign (name&, name&);
+ // // Compare two values. Only needs to be provided by simple types.
+ // //
+ // static int compare (const T&, const T&);
+ //
+ // static const build2::value_type value_type;
+ // };
- // Name cast. Can only be called after assign() above returned true.
+ // Convert name to a simple value. Throw invalid_argument (without any
+ // message) if the name is not a valid representation of value (in which
+ // case the name remains unchanged for diagnostics). The second version is
+ // called for a pair.
//
- template <typename T> typename value_traits<T>::type as (name&);
- template <typename T> typename value_traits<T>::const_type as (const name&);
+ template <typename T> T convert (name&&);
+ template <typename T> T convert (name&&, name&&);
- // bool
+ // Default implementations of the dtor/copy_ctor/copy_assing callbacks for
+ // types that are stored directly in value::data_ and the provide all the
+ // necessary functions (copy/move ctor and assignment operator).
//
- template <typename D>
- struct bool_value
- {
- explicit
- operator bool () const {return d->value[0] == 't';}
+ template <typename T>
+ static void
+ default_dtor (value&);
- bool_value&
- operator= (bool v)
- {
- d->value = v ? "true" : "false";
- return *this;
- }
+ template <typename T>
+ static void
+ default_copy_ctor (value&, const value&, bool);
- bool_value&
- operator+= (bool v)
- {
- if (!*this && v)
- d->value = "true";
- return *this;
- }
+ template <typename T>
+ static void
+ default_copy_assign (value&, const value&, bool);
+
+ // Default implementations of the assign/append/prepend callbacks for simple
+ // types. They call value_traits<T>::convert() and then pass the result to
+ // value_traits<T>::assign()/append()/prepend(). As a result, it may not be
+ // the most efficient way to do it. If the empty template parameter is true,
+ // then an empty names sequence is converted to a default-constructed T. And
+ // if false, then an empty value is not allowed.
+ //
+ template <typename T, bool empty>
+ static bool
+ simple_assign (value&, names&&, const variable&);
- // Implementation details.
- //
- public:
- explicit bool_value (D& d): d (&d) {}
+ template <typename T, bool empty>
+ static bool
+ simple_append (value&, names&&, const variable&);
- bool_value (const bool_value&) = delete;
- bool_value& operator= (const bool_value&) = delete; // Rebind or deep?
+ template <typename T, bool empty>
+ static bool
+ simple_prepend (value&, names&&, const variable&);
- bool_value (bool_value&&) = default;
- bool_value& operator= (bool_value&&) = delete;
+ // Default implementations of the reverse callback for simple types that
+ // calls value_traits<T>::reverse() and adds the result to the vector. As a
+ // result, it may not be the most efficient way to do it.
+ //
+ template <typename T>
+ static names_view
+ simple_reverse (const value&, names&);
- D* d; // name
- };
+ // Default implementations of the compare callback for simple types that
+ // calls value_traits<T>::compare().
+ //
+ template <typename T>
+ static int
+ simple_compare (const value&, const value&);
+ // bool
+ //
template <>
struct value_traits<bool>
{
- using type = bool_value<name>;
- using const_type = bool_value<const name>;
-
- static type as (name& n) {return type (n);}
- static const_type as (const name& n) {return const_type (n);}
+ static_assert (sizeof (bool) <= value::size_, "insufficient space");
- static type as (value&);
- static const_type as (const value&);
-
- static bool assign (name&, name*);
- static void assign (value&, bool);
- static void append (value&, bool);
+ static bool convert (name&&, name*);
+ static bool assign (value&, bool);
+ static bool append (value&, bool); // OR.
+ static name reverse (bool x) {return name (x ? "true" : "false");}
+ static int compare (bool, bool);
static const build2::value_type value_type;
};
- extern const value_type* bool_type;
-
// string
//
template <>
struct value_traits<string>
{
- using type = string&;
- using const_type = const string&;
-
- static type as (name& n) {return n.value;}
- static const_type as (const name& n) {return n.value;}
+ static_assert (sizeof (string) <= value::size_, "insufficient space");
- static type as (value&);
- static const_type as (const value&);
-
- static bool assign (name&, name*);
- static void assign (value&, string);
- static void append (value&, string);
+ static string convert (name&&, name*);
+ static bool assign (value&, string&&);
+ static bool append (value&, string&&);
+ static bool prepend (value&, string&&);
+ static name reverse (const string& x) {return name (x);}
+ static int compare (const string&, const string&);
static const build2::value_type value_type;
};
- extern const value_type* string_type; //@@ Get rid (and others).
-
// dir_path
//
template <>
struct value_traits<dir_path>
{
- using type = dir_path&;
- using const_type = const dir_path&;
-
- static type as (name& n) {return n.dir;}
- static const_type as (const name& n) {return n.dir;}
-
- static type as (value&);
- static const_type as (const value&);
+ static_assert (sizeof (dir_path) <= value::size_, "insufficient space");
- static bool assign (name&, name*);
- static void assign (value&, dir_path);
- static void append (value&, dir_path);
+ static dir_path convert (name&&, name*);
+ static bool assign (value&, dir_path&&);
+ static bool append (value&, dir_path&&); // operator/
+ static bool prepend (value&, dir_path&&); // operator/
+ static name reverse (const dir_path& x) {return name (x);}
+ static int compare (const dir_path&, const dir_path&);
static const build2::value_type value_type;
};
- extern const value_type* dir_path_type;
-
// name
//
template <>
struct value_traits<name>
{
- using type = name&;
- using const_type = const name&;
-
- static type as (name& n) {return n;}
- static const_type as (const name& n) {return n;}
-
- static type as (value&);
- static const_type as (const value&);
+ static_assert (sizeof (name) <= value::size_, "insufficient space");
- static bool assign (name&, name* r) {return r == nullptr;}
- static void assign (value&, name);
- static void append (value&, name) = delete;
+ static name convert (name&&, name*);
+ static bool assign (value&, name&&);
+ static bool append (value&, name&&);
+ static bool prepend (value&, name&&);
+ static name reverse (const name& x) {return x;}
+ static int compare (const name&, const name&);
static const build2::value_type value_type;
};
- extern const value_type* name_type;
-
// vector<T>
//
- template <typename T, typename D>
- struct vector_value
- {
- using size_type = typename D::size_type;
-
- using value_type = typename value_traits<T>::type;
- using const_value_type = typename value_traits<T>::const_type;
-
- template <typename I, typename V, typename R>
- struct iterator_impl: I
- {
- using value_type = V;
- using pointer = value_type*;
- using reference = R;
- using difference_type = typename I::difference_type;
-
- iterator_impl () = default;
- iterator_impl (const I& i): I (i) {}
-
- // Note: operator->() is unavailable if R is a value.
- //
- reference operator* () const {return as<T> (I::operator* ());}
- pointer operator-> () const {return &as<T> (I::operator* ());}
- reference operator[] (difference_type n) const
- {
- return as<T> (I::operator[] (n));
- }
- };
-
- // Make iterator the same as const_iterator if our data type is const.
- //
- using const_iterator =
- iterator_impl<names::const_iterator, const T, const_value_type>;
- using iterator = typename std::conditional<
- std::is_const<D>::value,
- const_iterator,
- iterator_impl<names::iterator, T, value_type>>::type;
-
- public:
- vector_value&
- operator= (vector<T> v) {assign (move (v)); return *this;}
-
- vector_value&
- assign (vector<T>);
-
- template <typename D1>
- vector_value&
- assign (const vector_value<T, D1>&);
-
- template <typename D1>
- vector_value&
- append (const vector_value<T, D1>&);
-
- public:
- bool empty () const {return d->empty ();}
- size_type size () const {return d->size ();}
-
- value_type operator[] (size_type i) {return as<T> ((*d)[i]);}
- const_value_type operator[] (size_type i) const {return as<T> ((*d)[i]);}
-
- iterator begin () {return iterator (d->begin ());}
- iterator end () {return iterator (d->end ());}
-
- const_iterator begin () const {return const_iterator (d->begin ());}
- const_iterator end () const {return const_iterator (d->end ());}
-
- const_iterator cbegin () const {return const_iterator (d->begin ());}
- const_iterator cend () const {return const_iterator (d->end ());}
-
- // Implementation details.
- //
- public:
- explicit vector_value (D& d): d (&d) {}
-
- vector_value (const vector_value&) = delete;
- vector_value& operator= (const vector_value&) = delete; // Rebind or deep?
-
- vector_value (vector_value&&) = default;
- vector_value& operator= (vector_value&&) = default; //@@ TMP
-
- explicit vector_value (nullptr_t): d (nullptr) {} //@@ TMP
-
- D* d; // names
- };
-
template <typename T>
struct value_traits<vector<T>>
{
- using type = vector_value<T, names>;
- using const_type = vector_value<T, const names>;
-
- static type as (value&);
- static const_type as (const value&);
+ static_assert (sizeof (vector<T>) <= value::size_, "insufficient space");
- template <typename V> static void assign (value&, V);
- template <typename V> static void append (value&, V);
+ static bool assign (value&, vector<T>&&);
+ static bool append (value&, vector<T>&&);
+ static bool prepend (value&, vector<T>&&);
static const string type_name;
static const build2::value_type value_type;
};
- template <typename T, typename D>
- struct value_traits<vector_value<T, D>>: value_traits<vector<T>> {};
-
- using strings_value = vector_value<string, names>;
- using const_strings_value = vector_value<string, const names>;
-
- extern const value_type* strings_type; // vector<string> aka strings
- extern const value_type* dir_paths_type; // vector<dir_path> aka dir_paths
- extern const value_type* names_type; // vector<name> aka names
-
// map<K, V>
//
- template <typename K, typename V, typename D>
- struct map_value
- {
- template <typename F, typename S>
- struct pair
- {
- using first_type = typename std::conditional<
- std::is_reference<F>::value,
- reference_wrapper<typename std::remove_reference<F>::type>,
- F>::type;
-
- using second_type = typename std::conditional<
- std::is_reference<S>::value,
- reference_wrapper<typename std::remove_reference<S>::type>,
- S>::type;
-
- first_type first;
- second_type second;
- };
-
- template <typename I, typename T>
- struct iterator_impl
- {
- using value_type = T;
- using pointer = value_type*;
- using reference = value_type; // Note: value.
- using difference_type = typename I::difference_type;
- using iterator_category = std::bidirectional_iterator_tag;
-
- iterator_impl () = default;
- iterator_impl (const I& i): i_ (i) {}
-
- pointer operator-> () const = delete;
- reference operator* () const
- {
- return value_type {as<K> (*i_), as<V> (*(i_ + 1))};
- }
-
- iterator_impl& operator++ () {i_ += 2; return *this;}
- iterator_impl operator++ (int) {auto r (*this); operator++ (); return r;}
-
- iterator_impl& operator-- () {i_ -= 2; return *this;}
- iterator_impl operator-- (int) {auto r (*this); operator-- (); return r;}
-
- bool operator== (const iterator_impl& y) const {return i_ == y.i_;}
- bool operator!= (const iterator_impl& y) const {return i_ != y.i_;}
-
- private:
- I i_;
- };
-
- using size_type = typename D::size_type;
-
- using value_type = pair<typename value_traits<K>::const_type,
- typename value_traits<V>::type>;
-
- using const_value_type = pair<typename value_traits<K>::const_type,
- typename value_traits<V>::const_type>;
-
- // Make iterator the same as const_iterator if our data type is const.
- //
- using const_iterator =
- iterator_impl<names::const_iterator, const_value_type>;
- using iterator = typename std::conditional<
- std::is_const<D>::value,
- const_iterator,
- iterator_impl<names::iterator, value_type>>::type;
-
-
- public:
- map_value&
- operator= (std::map<K, V> m) {assign (move (m)); return *this;}
-
- map_value&
- assign (std::map<K, V>);
-
- bool empty () const {return d->empty ();}
- size_type size () const {return d->size ();}
-
- iterator find (const K&);
- const_iterator find (const K&) const;
-
- iterator begin () {return iterator (d->begin ());}
- iterator end () {return iterator (d->end ());}
-
- const_iterator begin () const {return const_iterator (d->begin ());}
- const_iterator end () const {return const_iterator (d->end ());}
-
- // Implementation details.
- //
- public:
- explicit map_value (D& d): d (&d) {}
-
- map_value (const map_value&) = delete;
- map_value& operator= (const map_value&) = delete; // Rebind or deep?
-
- map_value (map_value&&) = default;
- map_value& operator= (map_value&&) = delete;
-
- D* d; // names
- };
-
template <typename K, typename V>
struct value_traits<std::map<K, V>>
{
- using type = map_value<K, V, names>;
- using const_type = map_value<K, V, const names>;
+ template <typename K1, typename V1> using map = std::map<K1, V1>;
- static type as (value&);
- static const_type as (const value&);
+ static_assert (sizeof (map<K, V>) <= value::size_, "insufficient space");
- template <typename M> static void assign (value&, M);
- template <typename M> static void append (value&, M);
+ static bool assign (value&, map<K, V>&&);
+ static bool append (value&, map<K, V>&&);
+ static bool prepend (value& v, map<K, V>&& x) {
+ return append (v, move (x));}
static const string type_name;
static const build2::value_type value_type;
};
-
- template <typename K, typename V, typename D>
- struct value_traits<map_value<K, V, D>>: value_traits<std::map<K, V>> {};
}
+// Variable map.
+//
namespace std
{
template <>
@@ -634,6 +492,12 @@ namespace build2
using variable_pool_base = std::unordered_set<variable>;
struct variable_pool: private variable_pool_base
{
+ const variable&
+ find (string name)
+ {
+ return find (name, nullptr, nullptr);
+ }
+
template <typename T>
const variable&
find (string name)
@@ -642,56 +506,23 @@ namespace build2
}
const variable&
- find (string name, const build2::value_type* t = nullptr)
+ find (string name, variable_visibility v)
{
- return find (name, nullptr, t);
+ return find (name, &v, nullptr);
}
+ template <typename T>
const variable&
- find (string name,
- variable_visibility v,
- const build2::value_type* t = nullptr)
+ find (string name, variable_visibility v)
{
- return find (name, &v, t);
+ return find (name, &v, &value_traits<T>::value_type);
}
using variable_pool_base::clear;
private:
const variable&
- find (string name,
- const variable_visibility* vv,
- const build2::value_type* t)
- {
- auto r (
- insert (
- variable {
- move (name),
- t,
- vv != nullptr ? *vv : variable_visibility::normal}));
- const variable& v (*r.first);
-
- // Update type?
- //
- if (!r.second && t != nullptr && v.type != t)
- {
- assert (v.type == nullptr);
- const_cast<variable&> (v).type = t; // Not changing the key.
- }
-
- // Change visibility? While this might at first seem like a bad idea,
- // it can happen that the variable lookup happens before any values
- // were set, in which case the variable will be entered with the
- // default visibility.
- //
- if (!r.second && vv != nullptr && v.visibility != *vv)
- {
- assert (v.visibility == variable_visibility::normal); // Default.
- const_cast<variable&> (v).visibility = *vv; // Not changing the key.
- }
-
- return v;
- }
+ find (string name, const variable_visibility*, const build2::value_type*);
};
extern variable_pool var_pool;
@@ -714,34 +545,6 @@ namespace build2
using const_iterator = iterator_adapter<map_type::const_iterator>;
- const value*
- find (const variable& var) const
- {
- auto i (m_.find (var));
- const value* r (i != m_.end () ? &i->second : nullptr);
-
- // First access after being assigned a type?
- //
- if (r != nullptr && var.type != nullptr && r->type != var.type)
- build2::assign (const_cast<value&> (*r), var.type, var);
-
- return r;
- }
-
- value*
- find (const variable& var)
- {
- auto i (m_.find (var));
- value* r (i != m_.end () ? &i->second : nullptr);
-
- // First access after being assigned a type?
- //
- if (r != nullptr && var.type != nullptr && r->type != var.type)
- build2::assign (*r, var.type, var);
-
- return r;
- }
-
lookup<const value>
operator[] (const variable& var) const
{
@@ -768,34 +571,29 @@ namespace build2
return operator[] (var_pool.find (name));
}
+ const value*
+ find (const variable&) const;
+
+ value*
+ find (const variable&);
+
// The second member in the pair indicates whether the new value (which
// will be NULL) was assigned.
//
pair<reference_wrapper<value>, bool>
- assign (const variable& var)
- {
- auto r (m_.emplace (var, value (var.type)));
- value& v (r.first->second);
-
- // First access after being assigned a type?
- //
- if (!r.second && var.type != nullptr && v.type != var.type)
- build2::assign (v, var.type, var);
-
- return make_pair (reference_wrapper<value> (v), r.second);
- }
+ assign (const variable&);
pair<reference_wrapper<value>, bool>
- assign (const string& name, const build2::value_type* type = nullptr)
+ assign (const string& name)
{
- return assign (var_pool.find (name, type));
+ return assign (var_pool.find (name));
}
template <typename T>
pair<reference_wrapper<value>, bool>
assign (const string& name)
{
- return assign (var_pool.find (name, &value_traits<T>::value_type));
+ return assign (var_pool.find<T> (name));
}
pair<const_iterator, const_iterator>
diff --git a/build2/variable.cxx b/build2/variable.cxx
index ce06d3e..c15a773 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -4,6 +4,8 @@
#include <build2/variable>
+#include <cstring> // memcmp()
+
#include <build2/diagnostics>
using namespace std;
@@ -12,23 +14,50 @@ namespace build2
{
// value
//
- void
- assign (value& v, const value_type* t, const variable& var)
+ value& value::
+ operator= (nullptr_t)
{
- if (v.type == nullptr)
+ if (!null ())
{
- v.type = t;
+ if (type == nullptr)
+ as<names> ().~names ();
+ else if (type->dtor != nullptr)
+ type->dtor (*this);
- if (v && t->assign != nullptr)
- v.state_ = t->assign (v.data_, var)
- ? value::state_type::filled
- : value::state_type::empty;
+ state = value_state::null;
+ }
+
+ return *this;
+ }
+
+ value::
+ value (value&& v)
+ : type (v.type), state (v.state)
+ {
+ if (!null ())
+ {
+ if (type == nullptr)
+ as<names> () = move (v).as<names> ();
+ else if (type->copy_ctor != nullptr)
+ type->copy_ctor (*this, v, true);
+ else
+ data_ = v.data_; // Copy as POD.
+ }
+ }
+
+ value::
+ value (const value& v)
+ : type (v.type), state (v.state)
+ {
+ if (!null ())
+ {
+ if (type == nullptr)
+ as<names> () = v.as<names> ();
+ else if (type->copy_ctor != nullptr)
+ type->copy_ctor (*this, v, false);
+ else
+ data_ = v.data_; // Copy as POD.
}
- else
- fail << "variable '" << var.name << "' type mismatch" <<
- info << "value '" << v.data_ << "' is " << v.type->name <<
- info << (t == var.type ? "variable" : "new type") << " is "
- << (var.type != nullptr ? var.type->name : "untyped");
}
value& value::
@@ -36,152 +65,264 @@ namespace build2
{
assert (type == nullptr || type == v.type);
- // Since the types are the same, we don't need to call
- // the callbacks.
- //
- type = v.type;
- state_ = v.state_;
- data_ = move (v.data_);
+ if (this != &v)
+ {
+ // Prepare the receiving value.
+ //
+ if (type == nullptr && v.type != nullptr)
+ {
+ if (!null ())
+ *this = nullptr;
- return *this;
- }
+ type = v.type;
+ }
+
+ // Now our types are the same. If the receiving value is NULL, then call
+ // copy_ctor() instead of copy_assign().
+ //
+ if (type == nullptr)
+ as<names> () = move (v).as<names> ();
+ else if (auto f = null () ? type->copy_ctor : type->copy_assign)
+ f (*this, v, true);
+ else
+ data_ = v.data_; // Assign as POD.
+
+ state = v.state;
+ }
- value& value::
- append (value v, const variable& var)
- {
- assert (type == v.type);
- append (move (v.data_), var);
return *this;
}
value& value::
- prepend (value v, const variable& var)
+ operator= (const value& v)
{
- assert (type == v.type);
- prepend (move (v.data_), var);
+ assert (type == nullptr || type == v.type);
+
+ if (this != &v)
+ {
+ // Prepare the receiving value.
+ //
+ if (type == nullptr && v.type != nullptr)
+ {
+ if (!null ())
+ {
+ reinterpret_cast<names&> (data_).~names ();
+ state = value_state::null;
+ }
+
+ type = v.type;
+ }
+
+ // Now our types are the same. If the receiving value is NULL, then call
+ // copy_ctor() instead of copy_assign().
+ //
+ if (type == nullptr)
+ as<names> () = v.as<names> ();
+ else if (auto f = null () ? type->copy_ctor : type->copy_assign)
+ f (*this, v, false);
+ else
+ data_ = v.data_; // Assign as POD.
+
+ state = v.state;
+ }
+
return *this;
}
void value::
- append (names v, const variable& var)
+ assign (names&& ns, const variable& var)
{
- // Treat append to NULL as assign.
- //
- if (!null () && type != nullptr && type->append != nullptr)
+ assert (type == nullptr || type->assign != nullptr);
+
+ bool r;
+
+ if (type == nullptr)
{
- state_ = type->append (data_, move (v), var)
- ? state_type::filled
- : state_type::empty;
- return;
- }
+ names* p;
+
+ if (null ())
+ p = new (&data_) names (move (ns));
+ else
+ {
+ p = &as<names> ();
+ *p = move (ns);
+ }
- if (data_.empty ())
- data_ = move (v);
+ r = !p->empty ();
+ }
else
- data_.insert (data_.end (),
- make_move_iterator (v.begin ()),
- make_move_iterator (v.end ()));
-
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
+ r = type->assign (*this, move (ns), var);
+
+ state = r ? value_state::filled : value_state::empty;
}
void value::
- prepend (names v, const variable& var)
+ append (names&& ns, const variable& var)
{
- // Reduce to append.
- //
- if (!null () && type != nullptr && type->append != nullptr)
+ bool r;
+
+ if (type == nullptr)
{
- state_ = type->append (v, move (data_), var)
- ? state_type::filled
- : state_type::empty;
- swap (data_, v);
- return;
- }
+ names* p;
- if (data_.empty ())
- data_ = move (v);
+ if (null ())
+ p = new (&data_) names (move (ns));
+ else
+ {
+ p = &as<names> ();
+
+ if (p->empty ())
+ *p = move (ns);
+ else if (!ns.empty ())
+ {
+ p->insert (p->end (),
+ make_move_iterator (ns.begin ()),
+ make_move_iterator (ns.end ()));
+ }
+ }
+
+ r = !p->empty ();
+ }
else
{
- v.insert (v.end (),
- make_move_iterator (data_.begin ()),
- make_move_iterator (data_.end ()));
- swap (data_, v);
+ if (type->append == nullptr)
+ fail << type->name << " value in variable " << var.name
+ << " cannot be appended to";
+
+ r = type->append (*this, move (ns), var);
}
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
+ state = r ? value_state::filled : value_state::empty;
}
- // bool value
- //
- bool value_traits<bool>::
- assign (name& n, name* r)
+ void value::
+ prepend (names&& ns, const variable& var)
{
- if (r == nullptr && n.simple ())
+ bool r;
+
+ if (type == nullptr)
{
- const string& s (n.value);
+ names* p;
- if (s == "true" || s == "false")
- return true;
+ if (null ())
+ p = new (&data_) names (move (ns));
+ else
+ {
+ p = &as<names> ();
+
+ if (p->empty ())
+ *p = move (ns);
+ else if (!ns.empty ())
+ {
+ ns.insert (ns.end (),
+ make_move_iterator (p->begin ()),
+ make_move_iterator (p->end ()));
+ p->swap (ns);
+ }
+ }
+
+ r = !p->empty ();
+ }
+ else
+ {
+ if (type->prepend == nullptr)
+ fail << type->name << " value in variable " << var.name
+ << " cannot be prepended to";
+
+ r = type->prepend (*this, move (ns), var);
}
- return false;
+ state = r ? value_state::filled : value_state::empty;
}
- static bool
- bool_assign (names& v, const variable& var)
+ bool
+ operator== (const value& x, const value& y)
{
- // Verify the value is either "true" or "false".
- //
- if (v.size () == 1)
- {
- name& n (v.front ());
+ assert (x.type == y.type);
- if (assign<bool> (n))
- return true;
- }
+ if (x.state != y.state)
+ return false;
+
+ if (x.null ())
+ return true;
+
+ if (x.type == nullptr)
+ return x.as<names> () == y.as<names> ();
- fail << "invalid bool variable '" << var.name << "' value '" << v << "'";
- return false;
+ if (x.type->compare == nullptr)
+ return memcmp (&x.data_, &y.data_, x.type->size) == 0;
+
+ return x.type->compare (x, y) == 0;
}
- static bool
- bool_append (names& v, names a, const variable& var)
+ void
+ typify (value& v, const value_type& t, const variable& var)
{
- // Translate append to OR.
- //
- bool_assign (a, var); // Verify "true" or "false".
+ if (v.type == nullptr)
+ {
+ if (!v.null ())
+ {
+ // Note: the order in which we do things here is important.
+ //
+ names ns (move (v).as<names> ());
+ v = nullptr;
+ v.type = &t;
+ v.assign (move (ns), var);
+ }
+ else
+ v.type = &t;
+ }
+ else if (v.type != &t)
+ {
+ fail << "variable " << var.name << " type mismatch" <<
+ info << "value type is " << v.type->name <<
+ info << (&t == var.type ? "variable" : "new") << " type is " << t.name;
+ }
+ }
- if (a.front ().value[0] == 't' && v.front ().value[0] == 'f')
- v = move (a);
+ // bool value
+ //
+ bool value_traits<bool>::
+ convert (name&& n, name* r)
+ {
+ if (r == nullptr && n.simple ())
+ {
+ const string& s (n.value);
+
+ if (s == "true")
+ return true;
+
+ if (s == "false")
+ return false;
+
+ // Fall through.
+ }
- return true;
+ throw invalid_argument (string ());
}
const value_type value_traits<bool>::value_type
{
"bool",
- &bool_assign,
- &bool_append
+ sizeof (bool),
+ nullptr, // No dtor (POD).
+ nullptr, // No copy_ctor (POD).
+ nullptr, // No copy_assign (POD).
+ &simple_assign<bool, false>, // No empty value.
+ &simple_append<bool, false>,
+ &simple_append<bool, false>, // Prepend same as append.
+ &simple_reverse<bool>,
+ nullptr, // No cast (cast data_ directly).
+ nullptr // No compare (compare as POD).
};
- const value_type* bool_type = &value_traits<bool>::value_type;
-
// string value
//
- bool value_traits<string>::
- assign (name& n, name* r)
+ string value_traits<string>::
+ convert (name&& n, name* r)
{
// The goal is to reverse the name into its original representation. The
- // code is a bit convoluted because we try to avoid extra allocation for
+ // code is a bit convoluted because we try to avoid extra allocations for
// the common cases (unqualified, unpaired simple name or directory).
//
@@ -189,215 +330,194 @@ namespace build2
//
if (!(n.simple (true) || n.directory (true)) ||
!(r == nullptr || r->simple (true) || r->directory (true)))
- return false;
+ throw invalid_argument (string ());
+
+ string s;
if (n.directory (true))
{
- n.value = move (n.dir).string (); // Move string out of path.
+ s = move (n.dir).string (); // Move string out of path.
// Add / back to the end of the path unless it is already there. Note
// that the string cannot be empty (n.directory () would have been
// false).
//
- if (!dir_path::traits::is_separator (n.value[n.value.size () - 1]))
- n.value += '/';
+ if (!dir_path::traits::is_separator (s[s.size () - 1]))
+ s += '/';
}
+ else
+ s.swap (n.value);
// Convert project qualification to its string representation.
//
if (n.qualified ())
{
- string s (*n.proj);
- s += '%';
- s += n.value;
- s.swap (n.value);
+ string p (*n.proj);
+ p += '%';
+ p += s;
+ p.swap (s);
}
// The same for the RHS of a pair, if we have one.
//
if (r != nullptr)
{
- n.value += '@';
+ s += '@';
if (r->qualified ())
{
- n.value += *r->proj;
- n.value += '%';
+ s += *r->proj;
+ s += '%';
}
if (r->directory (true))
{
- n.value += r->dir.string ();
+ s += r->dir.string ();
- if (!dir_path::traits::is_separator (n.value[n.value.size () - 1]))
- n.value += '/';
+ if (!dir_path::traits::is_separator (s[s.size () - 1]))
+ s += '/';
}
else
- n.value += r->value;
- }
-
- return true;
- }
-
- static bool
- string_assign (names& v, const variable& var)
- {
- // Verify/convert the value is/to a single simple name.
- //
- if (v.empty ())
- {
- v.emplace_back (name ()); // Canonical empty string representation.
- return false;
- }
- else if (v.size () == 1)
- {
- name& n (v.front ());
-
- if (assign<string> (n))
- return !n.value.empty ();
+ s += r->value;
}
- fail << "invalid string variable '" << var.name << "' value '" << v << "'";
- return false;
- }
-
- static bool
- string_append (names& v, names a, const variable& var)
- {
- // Translate append to string concatenation.
- //
- string_assign (a, var); // Verify/convert value is/to string.
-
- if (v.front ().value.empty ())
- v = move (a);
- else
- v.front ().value += a.front ().value;
-
- return !v.front ().value.empty ();
+ return s;
}
const value_type value_traits<string>::value_type
{
"string",
- &string_assign,
- &string_append
+ sizeof (string),
+ &default_dtor<string>,
+ &default_copy_ctor<string>,
+ &default_copy_assign<string>,
+ &simple_assign<string, true>, // Allow empty strings.
+ &simple_append<string, true>,
+ &simple_prepend<string, true>,
+ &simple_reverse<string>,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<string>
};
- const value_type* string_type = &value_traits<string>::value_type;
-
// dir_path value
//
- bool value_traits<dir_path>::
- assign (name& n, name* r)
+ dir_path value_traits<dir_path>::
+ convert (name&& n, name* r)
{
- if (r != nullptr)
- return false;
-
- if (n.directory ())
- return true;
-
- if (n.simple ())
+ if (r == nullptr)
{
- try
+ if (n.directory ())
+ return move (n.dir);
+
+ if (n.simple ())
{
- n.dir = n.empty () ? dir_path () : dir_path (move (n.value));
- n.value.clear ();
- return true;
+ try
+ {
+ return dir_path (move (n.value));
+ }
+ catch (const invalid_path&) {} // Fall through.
}
- catch (const invalid_path&) {} // Fall through.
+
+ // Fall through.
}
- return false;
+ throw invalid_argument (string ());
}
- static bool
- dir_path_assign (names& v, const variable& var)
+ const value_type value_traits<dir_path>::value_type
{
- // Verify/convert the value is/to a single directory name.
- //
- if (v.empty ())
- {
- v.emplace_back (dir_path ()); // Canonical empty path representation.
- return false;
- }
- else if (v.size () == 1)
- {
- name& n (v.front ());
+ "dir_path",
+ sizeof (dir_path),
+ &default_dtor<dir_path>,
+ &default_copy_ctor<dir_path>,
+ &default_copy_assign<dir_path>,
+ &simple_assign<dir_path, true>, // Allow empty paths.
+ &simple_append<dir_path, true>,
+ &simple_prepend<dir_path, true>,
+ &simple_reverse<dir_path>,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<dir_path>
+ };
- if (assign<dir_path> (n))
- return !n.dir.empty ();
- }
+ // name value
+ //
+ name value_traits<name>::
+ convert (name&& n, name* r)
+ {
+ if (r != nullptr)
+ throw invalid_argument (string ());
- fail << "invalid dir_path variable '" << var.name << "' "
- << "value '" << v << "'";
- return false;
+ return move (n);
}
- static bool
- dir_path_append (names& v, names a, const variable& var)
+ static names_view
+ name_reverse (const value& v, names&)
{
- // Translate append to path concatenation.
- //
- dir_path_assign (a, var); // Verify/convert value is/to dir_path.
-
- dir_path& d (a.front ().dir);
- if (d.relative ())
- return !(v.front ().dir /= d).empty ();
- else
- fail << "append of absolute path '" << d << "' to dir_path variable "
- << var.name;
-
- return false;
+ return names_view (&v.as<name> (), 1);
}
- const value_type value_traits<dir_path>::value_type
+ const value_type value_traits<name>::value_type
{
- "dir_path",
- &dir_path_assign,
- &dir_path_append
+ "name",
+ sizeof (name),
+ &default_dtor<name>,
+ &default_copy_ctor<name>,
+ &default_copy_assign<name>,
+ &simple_assign<name, true>, // Allow empty names.
+ nullptr, // Append not supported.
+ nullptr, // Prepend not supported.
+ &name_reverse,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<name>
};
- const value_type* dir_path_type = &value_traits<dir_path>::value_type;
+ // variable_pool
+ //
+ variable_pool var_pool;
- // name value
+ // variable_map
//
- static bool
- name_assign (names& v, const variable& var)
+ const value* variable_map::
+ find (const variable& var) const
{
- // Verify the value is a single name.
+ auto i (m_.find (var));
+ const value* r (i != m_.end () ? &i->second : nullptr);
+
+ // First access after being assigned a type?
//
- if (v.size () == 1)
- return v.front ().empty ();
+ if (r != nullptr && var.type != nullptr && r->type != var.type)
+ typify (const_cast<value&> (*r), *var.type, var);
- fail << "invalid string variable '" << var.name << "' value '" << v << "'";
- return false;
+ return r;
}
- static bool
- name_append (names&, names, const variable& var)
+ value* variable_map::
+ find (const variable& var)
{
- fail << "append to name variable '" << var.name << "'";
- return false;
+ auto i (m_.find (var));
+ value* r (i != m_.end () ? &i->second : nullptr);
+
+ // First access after being assigned a type?
+ //
+ if (r != nullptr && var.type != nullptr && r->type != var.type)
+ typify (*r, *var.type, var);
+
+ return r;
}
- const value_type value_traits<name>::value_type
+ pair<reference_wrapper<value>, bool> variable_map::
+ assign (const variable& var)
{
- "name",
- &name_assign,
- &name_append
- };
+ auto r (m_.emplace (var, value (var.type)));
+ value& v (r.first->second);
- const value_type* name_type = &value_traits<name>::value_type;
-
- // vector<T> value
- //
- const value_type* strings_type = &value_traits<strings>::value_type;
- const value_type* dir_paths_type = &value_traits<dir_paths>::value_type;
- const value_type* names_type = &value_traits<names>::value_type;
+ // First access after being assigned a type?
+ //
+ if (!r.second && var.type != nullptr && v.type != var.type)
+ typify (v, *var.type, var);
- // variable_set
- //
- variable_pool var_pool;
+ return make_pair (reference_wrapper<value> (v), r.second);
+ }
// variable_type_map
//
diff --git a/build2/variable.ixx b/build2/variable.ixx
index df3f0c5..a129835 100644
--- a/build2/variable.ixx
+++ b/build2/variable.ixx
@@ -2,371 +2,452 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+#include <type_traits> // is_same
+
namespace build2
{
// value
//
- template <typename T>
- inline void
- assign (value& v, const variable& var)
+ inline value& value::
+ operator= (reference_wrapper<value> v)
{
- auto t (&value_traits<T>::value_type);
+ return *this = v.get ();
+ }
- if (v.type != t)
- assign (v, t, var);
+ inline value& value::
+ operator= (reference_wrapper<const value> v)
+ {
+ return *this = v.get ();
}
template <typename T>
- inline typename value_traits<T>::type
- as (value& v)
+ inline value& value::
+ operator= (T v)
{
- return value_traits<T>::as (v);
+ assert (type == &value_traits<T>::value_type || type == nullptr);
+
+ // Prepare the receiving value.
+ //
+ if (type == nullptr)
+ {
+ if (!null ())
+ *this = nullptr;
+
+ type = &value_traits<T>::value_type;
+ }
+
+ state = value_traits<T>::assign (*this, move (v))
+ ? value_state::filled
+ : value_state::empty;
+
+ return *this;
}
template <typename T>
- inline typename value_traits<T>::const_type
- as (const value& v)
+ inline value& value::
+ operator+= (T v)
{
- return value_traits<T>::as (v);
+ assert (type == &value_traits<T>::value_type ||
+ (type == nullptr && null ()));
+
+ // Prepare the receiving value.
+ //
+ if (type == nullptr)
+ type = &value_traits<T>::value_type;
+
+ state = value_traits<T>::append (*this, move (v))
+ ? value_state::filled
+ : value_state::empty;
+
+ return *this;
}
- template <typename T>
inline bool
- assign (name& n)
+ operator!= (const value& x, const value& y)
{
- return value_traits<T>::assign (n, nullptr);
+ return !(x == y);
}
- template <typename T>
- inline bool
- assign (name& l, name& r)
+ template <>
+ inline const names&
+ cast (const value& v)
{
- return value_traits<T>::assign (l, &r);
+ // Note that it can still be a typed vector<names>.
+ //
+ assert (!v.null () &&
+ (v.type == nullptr || v.type == &value_traits<names>::value_type));
+ return v.as<names> ();
}
template <typename T>
- inline typename value_traits<T>::type
- as (name& n)
+ inline const T&
+ cast (const value& v)
{
- return value_traits<T>::as (n);
+ assert (!v.null () && v.type == &value_traits<T>::value_type);
+ return *static_cast<const T*> (v.type->cast == nullptr
+ ? static_cast<const void*> (&v.data_)
+ : v.type->cast (v));
}
template <typename T>
- inline typename value_traits<T>::const_type
- as (const name& n)
+ inline T&
+ cast (value& v)
{
- return value_traits<T>::as (n);
+ return const_cast<T&> (cast<T> (static_cast<const value&> (v)));
}
template <typename T>
- inline value& value::
- operator= (T v)
+ inline T&&
+ cast (value&& v)
{
- value_traits<T>::assign (*this, move (v));
- return *this;
+ return move (cast<T> (v)); // Forward to T&.
}
template <typename T>
- inline value& value::
- operator+= (T v)
+ inline void
+ typify (value& v, const variable& var)
{
- value_traits<T>::append (*this, move (v));
- return *this;
+ value_type& t (value_traits<T>::value_type);
+
+ if (v.type != &t)
+ typify (v, t, var);
}
- inline void value::
- assign (names v, const variable& var)
+ inline names_view
+ reverse (const value& v, names& storage)
{
- data_ = move (v);
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
+ assert (!v.null () &&
+ storage.empty () &&
+ (v.type == nullptr || v.type->reverse != nullptr));
+ return v.type == nullptr ? v.as<names> () : v.type->reverse (v, storage);
}
- // bool value
+ // value_traits
//
- inline bool_value<name> value_traits<bool>::
- as (value& v)
+ template <typename T>
+ inline T
+ convert (name&& n)
{
- assert (v.type == bool_type);
- return bool_value<name> (v.data_.front ());
+ return value_traits<T>::convert (move (n), nullptr);
}
- inline bool_value<const name> value_traits<bool>::
- as (const value& v)
+ template <typename T>
+ inline T
+ convert (name&& l, name&& r)
{
- assert (v.type == bool_type);
- return bool_value<const name> (v.data_.front ());
+ return value_traits<T>::convert (move (l), &r);
}
- inline void value_traits<bool>::
+ // bool value
+ //
+ inline bool value_traits<bool>::
assign (value& v, bool x)
{
if (v.null ())
- {
- if (v.type == nullptr)
- v.type = bool_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
- }
+ new (&v.data_) bool (x);
+ else
+ v.as<bool> () = x;
- as (v) = x;
- v.state_ = value::state_type::filled;
+ return true;
}
- inline void value_traits<bool>::
+ inline bool value_traits<bool>::
append (value& v, bool x)
{
+ // Logical OR.
+ //
if (v.null ())
- assign (v, x);
+ new (&v.data_) bool (x);
else
- as (v) += x; // Cannot be empty.
+ v.as<bool> () = v.as<bool> () || x;
+
+ return true;
}
- // string value
- //
- inline string& value_traits<string>::
- as (value& v)
+ inline int value_traits<bool>::
+ compare (bool l, bool r)
{
- assert (v.type == string_type);
- return v.data_.front ().value;
+ return l < r ? -1 : (l > r ? 1 : 0);
}
- inline const string& value_traits<string>::
- as (const value& v)
+ // string value
+ //
+ inline bool value_traits<string>::
+ assign (value& v, string&& x)
{
- assert (v.type == string_type);
- return v.data_.front ().value;
+ string* p;
+
+ if (v.null ())
+ p = new (&v.data_) string (move (x));
+ else
+ p = &(v.as<string> () = move (x));
+
+ return !p->empty ();
}
- inline void value_traits<string>::
- assign (value& v, string x)
+ inline bool value_traits<string>::
+ append (value& v, string&& x)
{
+ string* p;
+
if (v.null ())
+ p = new (&v.data_) string (move (x));
+ else
{
- if (v.type == nullptr)
- v.type = string_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
+ p = &v.as<string> ();
+
+ if (p->empty ())
+ p->swap (x);
+ else
+ *p += x;
}
- v.state_ = (as (v) = move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
+ return !p->empty ();
}
- inline void value_traits<string>::
- append (value& v, string x)
+ inline bool value_traits<string>::
+ prepend (value& v, string&& x)
{
+ string* p;
+
if (v.null ())
- assign (v, move (x));
+ new (&v.data_) string (move (x));
else
- v.state_ = (as (v) += move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
+ {
+ p = &v.as<string> ();
- // dir_path value
- //
- inline dir_path& value_traits<dir_path>::
- as (value& v)
- {
- assert (v.type == dir_path_type);
- return v.data_.front ().dir;
+ if (!p->empty ())
+ x += *p;
+
+ p->swap (x);
+ }
+
+ return !p->empty ();
}
- inline const dir_path& value_traits<dir_path>::
- as (const value& v)
+ inline int value_traits<string>::
+ compare (const string& l, const string& r)
{
- assert (v.type == dir_path_type);
- return v.data_.front ().dir;
+ return l.compare (r);
}
- inline void value_traits<dir_path>::
- assign (value& v, dir_path x)
+ // dir_path value
+ //
+ inline bool value_traits<dir_path>::
+ assign (value& v, dir_path&& x)
{
+ dir_path* p;
+
if (v.null ())
- {
- if (v.type == nullptr)
- v.type = dir_path_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
- }
+ p = new (&v.data_) dir_path (move (x));
+ else
+ p = &(v.as<dir_path> () = move (x));
- v.state_ = (as (v) = move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
+ return !p->empty ();
}
- inline void value_traits<dir_path>::
- append (value& v, dir_path x)
+ inline bool value_traits<dir_path>::
+ append (value& v, dir_path&& x)
{
+ dir_path* p;
+
if (v.null ())
- assign (v, move (x));
+ p = new (&v.data_) dir_path (move (x));
else
- v.state_ = (as (v) /= move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
+ {
+ p = &v.as<dir_path> ();
- // name value
- //
- inline name& value_traits<name>::
- as (value& v)
- {
- assert (v.type == name_type);
- return v.data_.front ();
- }
+ if (p->empty ())
+ p->swap (x);
+ else
+ *p /= x;
+ }
- inline const name& value_traits<name>::
- as (const value& v)
- {
- assert (v.type == name_type);
- return v.data_.front ();
+ return !p->empty ();
}
- inline void value_traits<name>::
- assign (value& v, name x)
+ inline bool value_traits<dir_path>::
+ prepend (value& v, dir_path&& x)
{
+ dir_path* p;
+
if (v.null ())
+ new (&v.data_) dir_path (move (x));
+ else
{
- if (v.type == nullptr)
- v.type = name_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
- }
+ p = &v.as<dir_path> ();
- v.state_ = (as (v) = move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
+ if (!p->empty ())
+ x /= *p;
- // vector<T> value
- //
- template <typename T, typename D>
- inline vector_value<T, D>& vector_value<T, D>::
- assign (vector<T> v)
- {
- d->clear ();
- d->insert (d->end (),
- make_move_iterator (v.begin ()),
- make_move_iterator (v.end ()));
- return *this;
+ p->swap (x);
+ }
+
+ return !p->empty ();
}
- template <typename T, typename D>
- template <typename D1>
- inline vector_value<T, D>& vector_value<T, D>::
- assign (const vector_value<T, D1>& v)
+ inline int value_traits<dir_path>::
+ compare (const dir_path& l, const dir_path& r)
{
- d->clear ();
- d->insert (d->end (), v.begin (), v.end ());
- return *this;
+ return l.compare (r);
}
- template <typename T, typename D>
- template <typename D1>
- inline vector_value<T, D>& vector_value<T, D>::
- append (const vector_value<T, D1>& v)
+ // name value
+ //
+ inline bool value_traits<name>::
+ assign (value& v, name&& x)
{
- d->insert (d->end (), v.begin (), v.end ());
- return *this;
+ name* p;
+
+ if (v.null ())
+ p = new (&v.data_) name (move (x));
+ else
+ p = &(v.as<name> () = move (x));
+
+ return !p->empty ();
}
- template <typename T>
- inline vector_value<T, names> value_traits<vector<T>>::
- as (value& v)
+ inline int value_traits<name>::
+ compare (const name& l, const name& r)
{
- assert (v.type == &value_traits<vector<T>>::value_type);
- return vector_value<T, names> (v.data_);
+ return l.compare (r);
}
+ // vector<T> value
+ //
template <typename T>
- inline vector_value<T, const names> value_traits<vector<T>>::
- as (const value& v)
+ inline bool value_traits<vector<T>>::
+ assign (value& v, vector<T>&& x)
{
- assert (v.type == &value_traits<vector<T>>::value_type);
- return vector_value<T, const names> (v.data_);
+ vector<T>* p;
+
+ if (v.null ())
+ p = new (&v.data_) vector<T> (move (x));
+ else
+ p = &(v.as<vector<T>> () = move (x));
+
+ return !p->empty ();
}
template <typename T>
- template <typename V>
- inline void value_traits<vector<T>>::
- assign (value& v, V x)
+ inline bool value_traits<vector<T>>::
+ append (value& v, vector<T>&& x)
{
+ vector<T>* p;
+
if (v.null ())
+ p = new (&v.data_) vector<T> (move (x));
+ else
{
- if (v.type == nullptr)
- v.type = &value_traits<vector<T>>::value_type;
- v.state_ = value::state_type::empty;
+ p = &v.as<vector<T>> ();
+
+ if (p->empty ())
+ p->swap (x);
+ else
+ p->insert (p->end (),
+ make_move_iterator (x.begin ()),
+ make_move_iterator (x.end ()));
}
- v.state_ = (as (v).assign (move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
+ return !p->empty ();
}
template <typename T>
- template <typename V>
- inline void value_traits<vector<T>>::
- append (value& v, V x)
+ inline bool value_traits<vector<T>>::
+ prepend (value& v, vector<T>&& x)
{
+ vector<T>* p;
+
if (v.null ())
- assign (v, move (x));
+ new (&v.data_) vector<T> (move (x));
else
- v.state_ = (as (v).append (move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
+ {
+ p = &v.as<vector<T>> ();
+
+ if (!p->empty ())
+ x.insert (x.end (),
+ make_move_iterator (p->begin ()),
+ make_move_iterator (p->end ()));
+
+ p->swap (x);
+ }
+
+ return !p->empty ();
}
// map<K, V> value
//
template <typename K, typename V>
- inline map_value<K, V, names> value_traits<std::map<K, V>>::
- as (value& v)
+ inline bool value_traits<std::map<K, V>>::
+ assign (value& v, map<K, V>&& x)
{
- assert ((v.type == &value_traits<std::map<K, V>>::value_type));
- return map_value<K, V, names> (v.data_);
- }
+ map<K, V>* p;
- template <typename K, typename V>
- inline map_value<K, V, const names> value_traits<std::map<K, V>>::
- as (const value& v)
- {
- assert ((v.type == &value_traits<std::map<K, V>>::value_type));
- return map_value<K, V, const names> (v.data_);
+ if (v.null ())
+ p = new (&v.data_) map<K, V> (move (x));
+ else
+ p = &(v.as<map<K, V>> () = move (x));
+
+ return !p->empty ();
}
template <typename K, typename V>
- template <typename M>
- inline void value_traits<std::map<K, V>>::
- assign (value& v, M x)
+ inline bool value_traits<std::map<K, V>>::
+ append (value& v, map<K, V>&& x)
{
+ map<K, V>* p;
+
if (v.null ())
+ p = new (&v.data_) map<K, V> (move (x));
+ else
{
- if (v.type == nullptr)
- v.type = &value_traits<std::map<K, V>>::value_type;
- v.state_ = value::state_type::empty;
+ p = &v.as<map<K, V>> ();
+
+ if (p->empty ())
+ p->swap (x);
+ else
+ // Note that this will only move values. Keys (being const) are still
+ // copied.
+ //
+ p->insert (p->end (),
+ make_move_iterator (x.begin ()),
+ make_move_iterator (x.end ()));
}
- v.state_ = (as (v).assign (move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
+ return !p->empty ();
}
- template <typename K, typename V>
- template <typename M>
- inline void value_traits<std::map<K, V>>::
- append (value& v, M x)
+ inline const variable& variable_pool::
+ find (string n, const variable_visibility* vv, const build2::value_type* t)
{
- if (v.null ())
- assign (v, move (x));
- else
- v.state_ = (as (v).append (move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
+ auto r (
+ insert (
+ variable {
+ move (n),
+ t,
+ vv != nullptr ? *vv : variable_visibility::normal}));
+ const variable& v (*r.first);
+
+ // Update type?
+ //
+ if (!r.second && t != nullptr && v.type != t)
+ {
+ assert (v.type == nullptr);
+ const_cast<variable&> (v).type = t; // Not changing the key.
+ }
+
+ // Change visibility? While this might at first seem like a bad idea,
+ // it can happen that the variable lookup happens before any values
+ // were set, in which case the variable will be entered with the
+ // default visibility.
+ //
+ if (!r.second && vv != nullptr && v.visibility != *vv)
+ {
+ assert (v.visibility == variable_visibility::normal); // Default.
+ const_cast<variable&> (v).visibility = *vv; // Not changing the key.
+ }
+
+ return v;
}
// variable_map::iterator_adapter
@@ -382,7 +463,7 @@ namespace build2
// First access after being assigned a type?
//
if (var.type != nullptr && val.type != var.type)
- build2::assign (const_cast<value&> (val), var.type, var);
+ typify (const_cast<value&> (val), *var.type, var);
return r;
}
@@ -398,7 +479,7 @@ namespace build2
// First access after being assigned a type?
//
if (var.type != nullptr && val.type != var.type)
- build2::assign (const_cast<value&> (val), var.type, var);
+ typify (const_cast<value&> (val), *var.type, var);
return p;
}
diff --git a/build2/variable.txx b/build2/variable.txx
index 7a0d53a..721e3c0 100644
--- a/build2/variable.txx
+++ b/build2/variable.txx
@@ -6,57 +6,232 @@
namespace build2
{
+ // value
+ //
+ template <typename T>
+ void
+ default_dtor (value& v)
+ {
+ v.as<T> ().~T ();
+ }
+
+ template <typename T>
+ void
+ default_copy_ctor (value& l, const value& r, bool m)
+ {
+ if (m)
+ new (&l.data_) T (move (const_cast<value&> (r).as<T> ()));
+ else
+ new (&l.data_) T (r.as<T> ());
+ }
+
+ template <typename T>
+ void
+ default_copy_assign (value& l, const value& r, bool m)
+ {
+ if (m)
+ l.as<T> () = move (const_cast<value&> (r).as<T> ());
+ else
+ l.as<T> () = r.as<T> ();
+ }
+
+ template <typename T, bool empty>
+ bool
+ simple_assign (value& v, names&& ns, const variable& var)
+ {
+ size_t n (ns.size ());
+
+ if (empty ? n <= 1 : n == 1)
+ {
+ try
+ {
+ return value_traits<T>::assign (
+ v,
+ (n == 0
+ ? T ()
+ : value_traits<T>::convert (move (ns.front ()), nullptr)));
+ }
+ catch (const invalid_argument&) {} // Fall through.
+ }
+
+ error << "invalid " << value_traits<T>::value_type.name
+ << " value '" << ns << "' in variable " << var.name;
+ throw failed ();
+ }
+
+ template <typename T, bool empty>
+ bool
+ simple_append (value& v, names&& ns, const variable& var)
+ {
+ size_t n (ns.size ());
+
+ if (empty ? n <= 1 : n == 1)
+ {
+ try
+ {
+ return value_traits<T>::append (
+ v,
+ (n == 0
+ ? T ()
+ : value_traits<T>::convert (move (ns.front ()), nullptr)));
+ }
+ catch (const invalid_argument&) {} // Fall through.
+ }
+
+ error << "invalid " << value_traits<T>::value_type.name
+ << " value '" << ns << "' in variable " << var.name;
+ throw failed ();
+ }
+
+ template <typename T, bool empty>
+ bool
+ simple_prepend (value& v, names&& ns, const variable& var)
+ {
+ size_t n (ns.size ());
+
+ if (empty ? n <= 1 : n == 1)
+ {
+ try
+ {
+ return value_traits<T>::prepend (
+ v,
+ (n == 0
+ ? T ()
+ : value_traits<T>::convert (move (ns.front ()), nullptr)));
+ }
+ catch (const invalid_argument&) {} // Fall through.
+ }
+
+ error << "invalid " << value_traits<T>::value_type.name
+ << " value '" << ns << "' in variable " << var.name;
+ throw failed ();
+ }
+
+ template <typename T>
+ names_view
+ simple_reverse (const value& v, names& s)
+ {
+ s.emplace_back (value_traits<T>::reverse (v.as<T> ()));
+ return s;
+ }
+
+ template <typename T>
+ int
+ simple_compare (const value& l, const value& r)
+ {
+ return value_traits<T>::compare (l.as<T> (), r.as<T> ());
+ }
+
// vector<T> value
//
+
template <typename T>
bool
- vector_assign (names& v, const variable& var)
+ vector_append (value& v, names&& ns, const variable& var)
{
- // Verify each element has valid value of T. Merge pairs.
+ vector<T>* p (v.null ()
+ ? new (&v.data_) vector<T> ()
+ : &v.as<vector<T>> ());
+
+ // Convert each element to T while merging pairs.
//
- for (auto i (v.begin ()); i != v.end (); )
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
{
name& n (*i);
+ name* r (n.pair ? &*++i : nullptr);
- if (n.pair)
+ try
{
- name& r (*++i);
-
- if (!assign<T> (n, r))
- fail << "invalid " << value_traits<T>::value_type.name
- << " pair '" << n << "'@'" << r << "'"
- << " in variable '" << var.name << "'";
-
- i = v.erase (i);
+ p->push_back (value_traits<T>::convert (move (n), r));
}
- else
+ catch (const invalid_argument&)
{
- if (!assign<T> (n))
- fail << "invalid " << value_traits<T>::value_type.name
- << " element '" << n << "' in variable '" << var.name << "'";
- ++i;
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<T>::value_type.name;
+
+ if (n.pair)
+ dr << " element pair '" << n << "'@'" << *r << "'";
+ else
+ dr << " element '" << n << "'";
+
+ dr << " in variable " << var.name;
}
}
- return !v.empty ();
+ return !p->empty ();
}
template <typename T>
bool
- vector_append (names& v, names a, const variable& var)
+ vector_assign (value& v, names&& ns, const variable& var)
{
- // Verify that what we are appending is valid.
+ if (!v.null ())
+ v.as<vector<T>> ().clear ();
+
+ return vector_append<T> (v, move (ns), var);
+ }
+
+ template <typename T>
+ bool
+ vector_prepend (value& v, names&& ns, const variable& var)
+ {
+ // Reduce to append.
//
- vector_assign<T> (a, var);
+ vector<T> t;
+ vector<T>* p;
- if (v.empty ())
- v = move (a);
+ if (v.null ())
+ p = new (&v.data_) vector<T> ();
else
- v.insert (v.end (),
- make_move_iterator (a.begin ()),
- make_move_iterator (a.end ()));
+ {
+ p = &v.as<vector<T>> ();
+ p->swap (t);
+ }
+
+ vector_append<T> (v, move (ns), var);
+
+ p->insert (p->end (),
+ make_move_iterator (t.begin ()),
+ make_move_iterator (t.end ()));
+
+ return !p->empty ();
+ }
+
+ template <typename T>
+ static names_view
+ vector_reverse (const value& v, names& s)
+ {
+ auto& vv (v.as<vector<T>> ());
+ s.reserve (vv.size ());
- return !v.empty ();
+ for (const T& x: vv)
+ s.push_back (value_traits<T>::reverse (x));
+
+ return s;
+ }
+
+ template <typename T>
+ static int
+ vector_compare (const value& l, const value& r)
+ {
+ auto& lv (l.as<vector<T>> ());
+ auto& rv (r.as<vector<T>> ());
+
+ auto li (lv.begin ()), le (lv.end ());
+ auto ri (rv.begin ()), re (rv.end ());
+
+ for (; li != le && ri != re; ++li, ++ri)
+ if (int r = value_traits<T>::compare (*li, *ri))
+ return r;
+
+ if (li == le && ri != re) // l shorter than r.
+ return -1;
+
+ if (ri == re && li != le) // r shorter than l.
+ return 1;
+
+ return 0;
}
template <typename T>
@@ -67,105 +242,127 @@ namespace build2
const value_type value_traits<vector<T>>::value_type
{
value_traits<vector<T>>::type_name.c_str (),
+ sizeof (vector<T>),
+ &default_dtor<vector<T>>,
+ &default_copy_ctor<vector<T>>,
+ &default_copy_assign<vector<T>>,
&vector_assign<T>,
- &vector_append<T>
+ &vector_append<T>,
+ &vector_prepend<T>,
+ &vector_reverse<T>,
+ nullptr, // No cast (cast data_ directly).
+ &vector_compare<T>
};
// map<K, V> value
//
- template <typename K, typename V, typename D>
- map_value<K, V, D>& map_value<K, V, D>::
- assign (std::map<K, V> m)
+ template <typename K, typename V>
+ bool
+ map_append (value& v, names&& ns, const variable& var)
{
- d->clear ();
- for (auto& p: m)
- {
- d->emplace_back (p.first); // Const, can't move.
- d->back ().pair = true;
- d->emplace_back (move (p.second));
- }
+ using std::map;
- return *this;
- }
+ map<K, V>* p (v.null ()
+ ? new (&v.data_) map<K, V> ()
+ : &v.as<map<K, V>> ());
- template <typename K, typename V, typename D>
- auto map_value<K, V, D>::
- find (const K& k) -> iterator
- {
- // @@ Scan backwards to handle duplicates.
+ // Verify we have a sequence of pairs and convert each lhs/rhs to K/V.
//
- for (auto i (d->rbegin ()); i != d->rend (); ++i)
- if (as<K> (*++i) == k)
- return iterator (--(i.base ()));
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& l (*i);
- return end ();
- }
+ if (!l.pair)
+ fail << value_traits<map<K, V>>::value_type.name << " key-value "
+ << "pair expected instead of '" << l << "' "
+ << "in variable " << var.name;
- template <typename K, typename V, typename D>
- auto map_value<K, V, D>::
- find (const K& k) const -> const_iterator
- {
- // @@ Scan backwards to handle duplicates.
- //
- for (auto i (d->rbegin ()); i != d->rend (); ++i)
- if (as<K> (*++i) == k)
- return const_iterator (--(i.base ()));
+ name& r (*++i); // Got to have the second half of the pair.
+
+ try
+ {
+ K k (value_traits<K>::convert (move (l), nullptr));
+
+ try
+ {
+ V v (value_traits<V>::convert (move (r), nullptr));
- return end ();
+ p->emplace (move (k), move (v));
+ }
+ catch (const invalid_argument&)
+ {
+ fail << "invalid " << value_traits<V>::value_type.name
+ << " element value '" << r << "' in variable " << var.name;
+ }
+ }
+ catch (const invalid_argument&)
+ {
+ fail << "invalid " << value_traits<K>::value_type.name
+ << " element key '" << l << "' in variable " << var.name;
+ }
+ }
+
+ return !p->empty ();
}
template <typename K, typename V>
bool
- map_assign (names& v, const variable& var)
+ map_assign (value& v, names&& ns, const variable& var)
{
- // Verify we have a sequence of pairs and each lhs/rhs is a valid
- // value of K/V.
- //
- for (auto i (v.begin ()); i != v.end (); ++i)
- {
- if (!i->pair)
- fail << value_traits<std::map<K, V>>::value_type.name << " key-value "
- << "pair expected instead of '" << *i << "' "
- << "in variable '" << var.name << "'";
+ using std::map;
- if (!assign<K> (*i))
- fail << "invalid " << value_traits<K>::value_type.name << " key "
- << "'" << *i << "' in variable '" << var.name << "'";
+ if (!v.null ())
+ v.as<map<K, V>> ().clear ();
- ++i; // Got to have the second half of the pair.
+ return map_append<K, V> (v, move (ns), var);
+ }
- if (!assign<V> (*i))
- fail << "invalid " << value_traits<V>::value_type.name << " value "
- << "'" << *i << "' in variable '" << var.name << "'";
- }
+ template <typename K, typename V>
+ static names_view
+ map_reverse (const value& v, names& s)
+ {
+ using std::map;
- //@@ When doing sorting, note that assign() can convert the
- // value.
+ auto& vm (v.as<map<K, V>> ());
+ s.reserve (2 * vm.size ());
- //@@ Is sorting really the right trade-off (i.e., insertion
- // vs search)? Perhaps linear search is ok?
+ for (const auto& p: vm)
+ {
+ s.push_back (value_traits<K>::reverse (p.first));
+ s.back ().pair = true;
+ s.push_back (value_traits<V>::reverse (p.second));
+ }
- return !v.empty ();
+ return s;
}
template <typename K, typename V>
- bool
- map_append (names& v, names a, const variable& var)
+ static int
+ map_compare (const value& l, const value& r)
{
- //@@ Not weeding out duplicates.
+ using std::map;
- // Verify that what we are appending is valid.
- //
- map_assign<K, V> (a, var);
+ auto& lm (l.as<map<K, V>> ());
+ auto& rm (r.as<map<K, V>> ());
- if (v.empty ())
- v = move (a);
- else
- v.insert (v.end (),
- make_move_iterator (a.begin ()),
- make_move_iterator (a.end ()));
+ auto li (lm.begin ()), le (lm.end ());
+ auto ri (rm.begin ()), re (rm.end ());
+
+ for (; li != le && ri != re; ++li, ++ri)
+ {
+ int r;
+ if ((r = value_traits<K>::compare (li->first, ri->first)) != 0 ||
+ (r = value_traits<V>::compare (li->second, ri->second)) != 0)
+ return r;
+ }
+
+ if (li == le && ri != re) // l shorter than r.
+ return -1;
+
+ if (ri == re && li != le) // r shorter than l.
+ return 1;
- return !v.empty ();
+ return 0;
}
template <typename K, typename V>
@@ -176,8 +373,16 @@ namespace build2
template <typename K, typename V>
const value_type value_traits<std::map<K, V>>::value_type
{
- value_traits<std::map<K, V>>::type_name.c_str (),
+ value_traits<map<K, V>>::type_name.c_str (),
+ sizeof (map<K, V>),
+ &default_dtor<map<K, V>>,
+ &default_copy_ctor<map<K, V>>,
+ &default_copy_assign<map<K, V>>,
&map_assign<K, V>,
- &map_append<K, V>
+ &map_append<K, V>,
+ &map_append<K, V>, // Prepend is the same as append.
+ &map_reverse<K, V>,
+ nullptr, // No cast (cast data_ directly).
+ &map_compare<K, V>
};
}