| 235 | } |
| 236 | |
| 237 | Errata |
| 238 | FeatureGroup::load(Config &cfg, YAML::Node const &node, std::initializer_list<FeatureGroup::Descriptor> const &ex_keys) |
| 239 | { |
| 240 | unsigned n_keys = node.size(); // Number of keys in @a node. |
| 241 | |
| 242 | Tracking::Info tracking_info[n_keys]; |
| 243 | Tracking tracking(node, tracking_info, n_keys); |
| 244 | |
| 245 | // Find the roots of extraction - these are the named keys actually in the node. |
| 246 | // Need to do this explicitly to transfer the flags, and to check for duplicates in @a ex_keys. |
| 247 | // It is not an error for a named key to be missing unless it's marked @c REQUIRED. |
| 248 | for (auto &d : ex_keys) { |
| 249 | auto tinfo = tracking.find(d._name); |
| 250 | if (nullptr != tinfo) { |
| 251 | return Errata( |
| 252 | S_ERROR, R"("INTERNAL ERROR: "{}" is used more than once in the extractor key list of the feature group for the node {}.)", |
| 253 | d._name, node.Mark()); |
| 254 | } |
| 255 | if (node[d._name]) { |
| 256 | tinfo = tracking.alloc(); |
| 257 | tinfo->_name = d._name; |
| 258 | } else if (d._flags[REQUIRED]) { |
| 259 | return Errata(S_ERROR, R"(The required key "{}" was not found in the node {}.)", d._name, node.Mark()); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | // Time to get the expressions and walk the references. Need to finalize the range before calling |
| 264 | // @c load_key as that can modify @a tracking_count. Also must avoid calling this on keys that |
| 265 | // are explicit but not required - need to fail on missing keys iff they're referenced, which is |
| 266 | // checked by @c load_key. The presence of required keys has already been verified. |
| 267 | for (auto info = tracking_info, limit = info + tracking._count; info < limit; ++info) { |
| 268 | auto &&[dummy, errata]{this->load_key(cfg, tracking, info->_name)}; |
| 269 | if (!errata.is_ok()) { |
| 270 | return std::move(errata); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | // Persist the tracking info, now that all the sizes are known. |
| 275 | _expr_info = cfg.alloc_span<ExprInfo>(tracking._count); |
| 276 | _expr_info.apply([](ExprInfo &info) { new (&info) ExprInfo; }); |
| 277 | |
| 278 | // If there are dependencies, allocate state to hold cached values. |
| 279 | // If any key was marked dependent, then @a _ref_count > 0. |
| 280 | if (_ref_count > 0) { |
| 281 | _ctx_state_span = cfg.reserve_ctx_storage(sizeof(State)); |
| 282 | _ordering = cfg.alloc_span<index_type>(_ref_count); |
| 283 | for (index_type idx = 0; idx < _ref_count; ++idx) { |
| 284 | _ordering[idx] = tracking._info[idx]._order_idx; |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | // Persist the keys by copying persistent data from the tracking data to config allocated space. |
| 289 | for (unsigned short idx = 0; idx < tracking._count; ++idx) { |
| 290 | Tracking::Info &src = tracking._info[idx]; |
| 291 | ExprInfo &dst = _expr_info[idx]; |
| 292 | dst._name = src._name; |
| 293 | dst._expr = std::move(src._expr); |
| 294 | dst._exf_idx = src._exf_idx; |