Refactor variable_info implementation
First off, this doesn't touch the actual functionality of variable_info. Instead, it refactors how its all put together: * Comments have been cleaned up and improved. * Formatting has been fixed. * Class/type names have been improved. * All info visitor and range helpers have been moved to a separate file. * apply_visitor now takes a parameter pack and owns its own visitor object. * as_range_visitor_base now takes a parameter pack and owns its own handler object * The variable_info policy types have been split into their own helper classes and their respective functionality implemented with static functions. This makes it clearer exactly how each policy works. * Simplified the use of type traits. The weird enable_if_non_const metastruct has been removed and a static_assert added to ensure variable_info_mutable is specialized correctly.
This commit is contained in:
parent
4f888e2073
commit
cdaa588eda
4 changed files with 849 additions and 698 deletions
|
@ -14,663 +14,218 @@
|
||||||
See the COPYING file for more details.
|
See the COPYING file for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Manage WML-variables.
|
|
||||||
*/
|
|
||||||
#include "variable_info.hpp"
|
#include "variable_info.hpp"
|
||||||
#include "game_config.hpp"
|
#include "variable_info_private.hpp"
|
||||||
#include "config_assign.hpp"
|
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <utility>
|
||||||
|
|
||||||
using namespace variable_info_detail;
|
namespace variable_info_implementation
|
||||||
|
|
||||||
/// general helpers
|
|
||||||
namespace
|
|
||||||
{
|
{
|
||||||
/// TConfig is eigher 'config' or 'const config'
|
/**
|
||||||
template<typename TConfig>
|
* Helper function to apply the result of a specified visitor to a variable_info object.
|
||||||
auto get_child_range(TConfig& cfg, const std::string& key, int start, int count) -> decltype(cfg.child_range(key))
|
*
|
||||||
{
|
* @tparam V Visitor type.
|
||||||
auto res = cfg.child_range(key);
|
* @tparam T Visitor argument parameter pack.
|
||||||
return { res.begin() + start, res.begin() + start + count};
|
*
|
||||||
}
|
* @param state Info state (the actual variable data).
|
||||||
|
* @param args Arguments to forward to visitor constructor.
|
||||||
void resolve_negative_value(int size, int& val)
|
*
|
||||||
{
|
* @returns Visitor output in its specified type.
|
||||||
if(val < 0)
|
* @throws std::range_error If @a state has an invalid type_ field.
|
||||||
{
|
*/
|
||||||
val = size + val;
|
template<typename V, typename... T>
|
||||||
}
|
typename V::result_t apply_visitor(typename V::param_t state, T&&... args)
|
||||||
//val is still < 0? We don't accept!
|
|
||||||
if(val < 0)
|
|
||||||
{
|
|
||||||
throw invalid_variablename_exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
typename maybe_const<vit, config>::type& get_child_at(typename maybe_const<vit, config>::type& cfg, const std::string& key, int index = 0);
|
|
||||||
|
|
||||||
template<>
|
|
||||||
config& get_child_at<vit_create_if_not_existent>(config& cfg, const std::string& key, int index)
|
|
||||||
{
|
|
||||||
assert(index >= 0);
|
|
||||||
// the 'create_if_not_existent' logic.
|
|
||||||
while(static_cast<int>(cfg.child_count(key)) <= index)
|
|
||||||
{
|
|
||||||
cfg.add_child(key);
|
|
||||||
}
|
|
||||||
return cfg.child(key, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
//helper variable for get_child_at<vit_const>
|
|
||||||
const config empty_const_cfg;
|
|
||||||
const config non_empty_const_cfg = config_of("_", config());
|
|
||||||
|
|
||||||
template<>
|
|
||||||
const config& get_child_at<vit_const>(const config& cfg, const std::string& key, int index)
|
|
||||||
{
|
|
||||||
assert(index >= 0);
|
|
||||||
//cfg.child_or_empty does not support index parameter
|
|
||||||
if(const config& child = cfg.child(key, index))
|
|
||||||
{
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return empty_const_cfg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
config& get_child_at<vit_throw_if_not_existent>(config& cfg, const std::string& key, int index)
|
|
||||||
{
|
|
||||||
assert(index >= 0);
|
|
||||||
if(config& child = cfg.child(key, index))
|
|
||||||
{
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw invalid_variablename_exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TVal>
|
|
||||||
config::attribute_value attribute_value_of(const TVal& val)
|
|
||||||
{
|
|
||||||
config::attribute_value v;
|
|
||||||
v = val;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @param visitor
|
|
||||||
/// TVisitor should have 4 methods:
|
|
||||||
/// from_named if the variable ended with a .somename
|
|
||||||
/// from_indexed if the variable enden with .somename[someindex]
|
|
||||||
/// from_temporary if the variable is a readonly value (.somename.length)
|
|
||||||
/// from_start if the variablename was previously empty, this can only happen
|
|
||||||
/// during calculate_value()
|
|
||||||
/// TVisitor should derive from variable_info_visitor(_const) which makes default implementation for these (as not supported)
|
|
||||||
template <typename TVisitor>
|
|
||||||
typename TVisitor::result_type apply_visitor(const TVisitor& visitor, typename TVisitor::param_type state)
|
|
||||||
{
|
|
||||||
switch(state.type_)
|
|
||||||
{
|
|
||||||
case state_start:
|
|
||||||
return visitor.from_start(state);
|
|
||||||
case state_named:
|
|
||||||
return visitor.from_named(state);
|
|
||||||
case state_indexed:
|
|
||||||
return visitor.from_indexed(state);
|
|
||||||
case state_temporary:
|
|
||||||
return visitor.from_temporary(state);
|
|
||||||
}
|
|
||||||
throw std::range_error("Failed to convert the TVisitor::param_type type");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <const variable_info_type vit, typename TResult>
|
|
||||||
class variable_info_visitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef TResult result_type;
|
|
||||||
typedef variable_info_state<vit>& param_type;
|
|
||||||
#define DEFAULTHANDLER(name) result_type name(param_type) const { throw invalid_variablename_exception(); }
|
|
||||||
DEFAULTHANDLER(from_start)
|
|
||||||
DEFAULTHANDLER(from_named)
|
|
||||||
DEFAULTHANDLER(from_indexed)
|
|
||||||
DEFAULTHANDLER(from_temporary)
|
|
||||||
#undef DEFAULTHANDLER
|
|
||||||
};
|
|
||||||
|
|
||||||
template <const variable_info_type vit, typename TResult>
|
|
||||||
class variable_info_visitor_const
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef TResult result_type;
|
|
||||||
typedef const variable_info_state<vit>& param_type;
|
|
||||||
#define DEFAULTHANDLER(name) result_type name(param_type) const { throw invalid_variablename_exception(); }
|
|
||||||
DEFAULTHANDLER(from_start)
|
|
||||||
DEFAULTHANDLER(from_named)
|
|
||||||
DEFAULTHANDLER(from_indexed)
|
|
||||||
DEFAULTHANDLER(from_temporary)
|
|
||||||
#undef DEFAULTHANDLER
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/// calculate_value() helpers
|
|
||||||
namespace
|
|
||||||
{
|
{
|
||||||
/// Parses a ']' terminated string.
|
static_assert(std::is_base_of<
|
||||||
/// This is a important optimisation of lexical_cast_default
|
info_visitor_base<
|
||||||
int parse_index(const char* index_str)
|
typename V::result_t,
|
||||||
{
|
typename std::remove_reference<typename V::param_t>::type>,
|
||||||
char* endptr;
|
V>::value, "Invalid visitor type.");
|
||||||
int res = strtol(index_str, &endptr, 10);
|
|
||||||
|
|
||||||
if (*endptr != ']' || res > int(game_config::max_loop) || endptr == index_str)
|
// Create the visitor.
|
||||||
{
|
V visitor(std::forward<T>(args)...);
|
||||||
throw invalid_variablename_exception();
|
|
||||||
}
|
switch(state.type_) {
|
||||||
return res;
|
case state_start:
|
||||||
|
return visitor.from_start(state);
|
||||||
|
case state_named:
|
||||||
|
return visitor.from_named(state);
|
||||||
|
case state_indexed:
|
||||||
|
return visitor.from_indexed(state);
|
||||||
|
case state_temporary:
|
||||||
|
return visitor.from_temporary(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a '.<key>' to the current variable
|
throw std::range_error("Failed to convert the TVisitor::param_t type");
|
||||||
template<const variable_info_type vit>
|
|
||||||
class get_variable_key_visitor
|
|
||||||
: public variable_info_visitor<vit, void>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
get_variable_key_visitor(const std::string& key) : key_(key) {
|
|
||||||
if (!config::valid_id(key_)) {
|
|
||||||
throw invalid_variablename_exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void from_named(typename get_variable_key_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
if(key_ == "length")
|
|
||||||
{
|
|
||||||
state.temp_val_ = state.child_->child_count(state.key_);
|
|
||||||
state.type_ = state_temporary;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return do_from_config(get_child_at<vit>(*state.child_, state.key_, 0), state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void from_start(typename get_variable_key_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return do_from_config(*state.child_, state);
|
|
||||||
}
|
|
||||||
void from_indexed(typename get_variable_key_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
//we do not support aaa[0].length
|
|
||||||
return do_from_config(get_child_at<vit>(*state.child_, state.key_, state.index_), state);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void do_from_config(typename maybe_const<vit, config>::type & cfg, typename get_variable_key_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
state.type_ = state_named;
|
|
||||||
state.key_ = key_;
|
|
||||||
state.child_ = &cfg;
|
|
||||||
}
|
|
||||||
const std::string& key_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// appens a [index] to the variable.
|
|
||||||
/// we only support from_named since [index][index2] or a.length[index] both doesn't make sense.
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
class get_variable_index_visitor
|
|
||||||
: public variable_info_visitor<vit, void>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
get_variable_index_visitor(int n) : n_(n) {}
|
|
||||||
void from_named(typename get_variable_index_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
state.index_ = n_;
|
|
||||||
resolve_negative_value(state.child_->child_count(state.key_), state.index_);
|
|
||||||
state.type_ = state_indexed;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
const int n_;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
/// as... visitors
|
} // end namespace variable_info_implementation
|
||||||
namespace
|
|
||||||
{
|
|
||||||
///tries to convert it to an (maybe const) attribute value
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
class as_skalar_visitor
|
|
||||||
: public variable_info_visitor_const<vit, typename maybe_const<vit, config::attribute_value>::type&>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typename as_skalar_visitor::result_type from_named(typename as_skalar_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return (*state.child_)[state.key_];
|
|
||||||
}
|
|
||||||
///Defined below for different cases.
|
|
||||||
typename as_skalar_visitor::result_type from_temporary(typename as_skalar_visitor::param_type state) const;
|
|
||||||
};
|
|
||||||
/// this type of value like '.length' are readonly values so we only support them for reading.
|
|
||||||
/// espacily we don't want to return non const references.
|
|
||||||
template<>
|
|
||||||
const config::attribute_value & as_skalar_visitor<vit_const>::from_temporary(as_skalar_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return state.temp_val_;
|
|
||||||
}
|
|
||||||
template<>
|
|
||||||
config::attribute_value & as_skalar_visitor<vit_create_if_not_existent>::from_temporary(as_skalar_visitor::param_type) const
|
|
||||||
{
|
|
||||||
throw invalid_variablename_exception();
|
|
||||||
}
|
|
||||||
template<>
|
|
||||||
config::attribute_value & as_skalar_visitor<vit_throw_if_not_existent>::from_temporary(as_skalar_visitor::param_type) const
|
|
||||||
{
|
|
||||||
throw invalid_variablename_exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// tries to convert to a (const) config&, unlike range based operation this also supports 'from_start'
|
using namespace variable_info_implementation;
|
||||||
/// Note: Currently getting the 'from_start' case here is impossible, because we always apply at least one string key.
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
class as_container_visitor
|
|
||||||
: public variable_info_visitor_const<vit, typename maybe_const<vit, config>::type&>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typename as_container_visitor::result_type from_named(typename as_container_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return get_child_at<vit>(*state.child_, state.key_, 0);
|
|
||||||
}
|
|
||||||
typename as_container_visitor::result_type from_start(typename as_container_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return *state.child_;
|
|
||||||
}
|
|
||||||
typename as_container_visitor::result_type from_indexed(typename as_container_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return get_child_at<vit>(*state.child_, state.key_, state.index_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// This currently isn't implemented as a range based operation because doing it on something like range
|
template<typename V>
|
||||||
// 2-5 on vit_const if child_ has only 4 elements would be too hard to implement.
|
variable_info<V>::variable_info(const std::string& varname, maybe_const_t<config, V>& vars) NOEXCEPT
|
||||||
template<const variable_info_type vit>
|
|
||||||
class as_array_visitor
|
|
||||||
: public variable_info_visitor_const<vit, typename maybe_const<vit, config::child_itors>::type>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typename as_array_visitor::result_type from_named(typename as_array_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return get_child_range(*state.child_, state.key_, 0, state.child_->child_count(state.key_));
|
|
||||||
}
|
|
||||||
typename as_array_visitor::result_type from_indexed(typename as_array_visitor::param_type state) const;
|
|
||||||
};
|
|
||||||
template<>
|
|
||||||
config::const_child_itors as_array_visitor<vit_const>::from_indexed(as_array_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
if (int(state.child_->child_count(state.key_)) <= state.index_)
|
|
||||||
{
|
|
||||||
return get_child_range(non_empty_const_cfg, "_", 0, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return get_child_range(*state.child_, state.key_, state.index_, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<>
|
|
||||||
config::child_itors as_array_visitor<vit_create_if_not_existent>::from_indexed(as_array_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
//Ensure we have a config at the given explicit position.
|
|
||||||
get_child_at<vit_create_if_not_existent>(*state.child_, state.key_, state.index_);
|
|
||||||
return get_child_range(*state.child_, state.key_, state.index_, 1);
|
|
||||||
}
|
|
||||||
template<>
|
|
||||||
config::child_itors as_array_visitor<vit_throw_if_not_existent>::from_indexed(as_array_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
//Ensure we have a config at the given explicit position.
|
|
||||||
get_child_at<vit_throw_if_not_existent>(*state.child_, state.key_, state.index_);
|
|
||||||
return get_child_range(*state.child_, state.key_, state.index_, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// range_based operations
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/// @tparam THandler a function
|
|
||||||
/// (config& cfg, const std::string& name, int range_begin, int range_end) -> THandler::result_type
|
|
||||||
/// that does the actual work on the range of children of cfg with name name.
|
|
||||||
/// Note that currently this is only used by the insert/append/replace/merge operations
|
|
||||||
/// so vit is always vit_create_if_not_existent
|
|
||||||
template<const variable_info_type vit, typename THandler>
|
|
||||||
class as_range_visitor_base
|
|
||||||
: public variable_info_visitor_const<vit, typename THandler::result_type>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
as_range_visitor_base(const THandler& handler) : handler_(handler) {}
|
|
||||||
typename as_range_visitor_base::result_type from_named(typename as_range_visitor_base::param_type state) const
|
|
||||||
{
|
|
||||||
return handler_(*state.child_, state.key_, 0, state.child_->child_count(state.key_));
|
|
||||||
}
|
|
||||||
typename as_range_visitor_base::result_type from_indexed(typename as_range_visitor_base::param_type state) const
|
|
||||||
{
|
|
||||||
return this->handler_(*state.child_, state.key_, state.index_, state.index_ + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
const THandler& handler_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
replaces the child in [startindex, endindex) with 'source'
|
|
||||||
'insert' and 'append' are subcases of this.
|
|
||||||
*/
|
|
||||||
class replace_range_h
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef config::child_itors result_type;
|
|
||||||
replace_range_h(std::vector<config>& source) : datasource_(source) { }
|
|
||||||
result_type operator()(config& child, const std::string& key, int startindex, int endindex) const
|
|
||||||
{
|
|
||||||
assert(endindex - startindex >= 0);
|
|
||||||
if (endindex > 0)
|
|
||||||
{
|
|
||||||
//NOTE: currently this is nonly called from as_range_visitor_base<vit_create_if_not_existent>
|
|
||||||
//based on that assumption we use get_child_at<vit_create_if_not_existent> here
|
|
||||||
//instead of making this a template<typename vit> function/type
|
|
||||||
get_child_at<vit_create_if_not_existent>(child, key, endindex - 1);
|
|
||||||
}
|
|
||||||
int size_diff = datasource_.size() - (endindex - startindex);
|
|
||||||
//remove configs first
|
|
||||||
while(size_diff < 0)
|
|
||||||
{
|
|
||||||
child.remove_child(key, startindex);
|
|
||||||
++size_diff;
|
|
||||||
}
|
|
||||||
size_t index = 0;
|
|
||||||
for(index = 0; index < static_cast<size_t>(size_diff); ++index)
|
|
||||||
{
|
|
||||||
child.add_child_at(key, config(), startindex + index).swap(datasource_[index]);
|
|
||||||
}
|
|
||||||
for(; index < datasource_.size(); ++index)
|
|
||||||
{
|
|
||||||
child.child(key, startindex + index).swap(datasource_[index]);
|
|
||||||
}
|
|
||||||
return get_child_range(child, key, startindex, datasource_.size());
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::vector<config>& datasource_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class insert_range_h : replace_range_h
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef config::child_itors result_type;
|
|
||||||
insert_range_h(std::vector<config>& source) : replace_range_h(source) { }
|
|
||||||
|
|
||||||
result_type operator()(config& child, const std::string& key, int startindex, int /*endindex*/) const
|
|
||||||
{
|
|
||||||
//insert == replace empty range with data.
|
|
||||||
return replace_range_h::operator()(child, key, startindex, startindex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class append_range_h : insert_range_h
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef config::child_itors result_type;
|
|
||||||
append_range_h(std::vector<config>& source) : insert_range_h(source) { }
|
|
||||||
result_type operator()(config& child, const std::string& key, int /*startindex*/, int /*endindex*/) const
|
|
||||||
{
|
|
||||||
//append == insert at end.
|
|
||||||
int inser_pos = child.child_count(key);
|
|
||||||
return insert_range_h::operator()(child, key, inser_pos, inser_pos /*ignored by insert_range_h*/);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class merge_range_h
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef void result_type;
|
|
||||||
merge_range_h(std::vector<config>& source) : datasource_(source) { }
|
|
||||||
void operator()(config& child, const std::string& key, int startindex, int /*endindex*/) const
|
|
||||||
{
|
|
||||||
//the merge_with function accepts only configs so we convert vector -> config.
|
|
||||||
config datatemp;
|
|
||||||
//Add emtpy config to 'shift' the merge to startindex
|
|
||||||
for(int index = 0; index < startindex; ++index)
|
|
||||||
{
|
|
||||||
datatemp.add_child(key);
|
|
||||||
}
|
|
||||||
//move datasource_ -> datatemp
|
|
||||||
for(size_t index = 0; index < datasource_.size(); ++index)
|
|
||||||
{
|
|
||||||
datatemp.add_child(key).swap(datasource_[index]);
|
|
||||||
}
|
|
||||||
child.merge_with(datatemp);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::vector<config>& datasource_;
|
|
||||||
};
|
|
||||||
}//annonymous namespace end based operations
|
|
||||||
/// misc
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
class clear_value_visitor
|
|
||||||
: public variable_info_visitor_const<vit, void>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
clear_value_visitor(bool only_tables) : only_tables_(only_tables) {}
|
|
||||||
void from_named(typename clear_value_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
if(!only_tables_)
|
|
||||||
{
|
|
||||||
state.child_->remove_attribute(state.key_);
|
|
||||||
}
|
|
||||||
state.child_->clear_children(state.key_);
|
|
||||||
}
|
|
||||||
void from_indexed(typename clear_value_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
state.child_->remove_child(state.key_, state.index_);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
bool only_tables_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
class exists_as_container_visitor
|
|
||||||
: public variable_info_visitor_const<vit, bool>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typename exists_as_container_visitor::result_type from_named(typename exists_as_container_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return state.child_->has_child(state.key_);
|
|
||||||
}
|
|
||||||
typename exists_as_container_visitor::result_type from_indexed(typename exists_as_container_visitor::param_type state) const
|
|
||||||
{
|
|
||||||
return state.child_->child_count(state.key_) > static_cast<size_t>(state.index_);
|
|
||||||
}
|
|
||||||
typename exists_as_container_visitor::result_type from_start(typename exists_as_container_visitor::param_type) const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
typename exists_as_container_visitor::result_type from_temporary(typename exists_as_container_visitor::param_type) const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
variable_info<vit>::variable_info(const std::string& varname, config_var& vars)
|
|
||||||
: name_(varname)
|
: name_(varname)
|
||||||
, state_(vars)
|
, state_(vars)
|
||||||
, valid_(true)
|
, valid_(true)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
calculate_value();
|
||||||
this->calculate_value();
|
} catch(const invalid_variablename_exception&) {
|
||||||
}
|
valid_ = false;
|
||||||
catch(const invalid_variablename_exception&)
|
|
||||||
{
|
|
||||||
this->valid_ = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
variable_info<vit>::~variable_info()
|
void variable_info<V>::calculate_value()
|
||||||
{
|
{
|
||||||
}
|
size_t previous_index = 0, name_size = name_.size();
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
for(size_t loop_index = 0; loop_index < name_size; loop_index++) {
|
||||||
void variable_info<vit>::calculate_value()
|
switch(name_[loop_index]) {
|
||||||
{
|
|
||||||
// this->state_ is initialized in the constructor.
|
|
||||||
size_t previous_index = 0;
|
|
||||||
size_t name_size = this->name_.size();
|
|
||||||
for(size_t loop_index = 0; loop_index < name_size; loop_index++)
|
|
||||||
{
|
|
||||||
switch(this->name_[loop_index])
|
|
||||||
{
|
|
||||||
case '.':
|
case '.':
|
||||||
case '[':
|
case '[':
|
||||||
// '.', '[' mark the end of a string key.
|
/* '.' and '[' mark the end of a string key.
|
||||||
// the result is oviously that '.' and '[' are
|
* The result is obviously that '.' and '[' are
|
||||||
// treated equally so 'aaa.9].bbbb[zzz.uu.7]'
|
* treated equally so 'aaa.9].bbbb[zzz.uu.7]'
|
||||||
// is interpreted as 'aaa[9].bbbb.zzz.uu[7]'
|
* is interpreted as 'aaa[9].bbbb.zzz.uu[7]'
|
||||||
// use is_valid_variable function for stricter variablename checking.
|
* Use is_valid_variable function for stricter variable name checking.
|
||||||
apply_visitor(get_variable_key_visitor<vit>(this->name_.substr(previous_index, loop_index-previous_index)), this->state_);
|
*/
|
||||||
|
apply_visitor<get_variable_key_visitor<V>>(
|
||||||
|
state_, name_.substr(previous_index, loop_index - previous_index));
|
||||||
|
|
||||||
previous_index = loop_index + 1;
|
previous_index = loop_index + 1;
|
||||||
break;
|
break;
|
||||||
case ']':
|
case ']':
|
||||||
// ']' marks the end of an integer key.
|
// ']' marks the end of an integer key.
|
||||||
apply_visitor(get_variable_index_visitor<vit>(parse_index(&this->name_[previous_index])), this->state_);
|
apply_visitor<get_variable_index_visitor<V>>(state_, parse_index(&name_[previous_index]));
|
||||||
//after ']' we always expect a '.' or the end of the string
|
|
||||||
//ignore the next char which is a '.'
|
// After ']' we always expect a '.' or the end of the string
|
||||||
|
// Ignore the next char which is a '.'
|
||||||
loop_index++;
|
loop_index++;
|
||||||
if(loop_index < this->name_.length() && this->name_[loop_index] != '.')
|
if(loop_index < name_.length() && name_[loop_index] != '.') {
|
||||||
{
|
|
||||||
throw invalid_variablename_exception();
|
throw invalid_variablename_exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
previous_index = loop_index + 1;
|
previous_index = loop_index + 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(previous_index != this->name_.length() + 1)
|
|
||||||
{
|
if(previous_index != name_.length() + 1) {
|
||||||
// the string ended not with ']'
|
// The string didn't end with ']'
|
||||||
// in this case we still didn't add the key behind the last '.'
|
// In this case we still didn't add the key behind the last '.'
|
||||||
apply_visitor(get_variable_key_visitor<vit>(this->name_.substr(previous_index)), this->state_);
|
apply_visitor<get_variable_key_visitor<V>>(state_, name_.substr(previous_index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
bool variable_info<vit>::explicit_index() const
|
bool variable_info<V>::explicit_index() const NOEXCEPT
|
||||||
{
|
{
|
||||||
throw_on_invalid();
|
throw_on_invalid();
|
||||||
return this->state_.type_ == state_start || this->state_.type_ == state_indexed;
|
return state_.type_ == state_start || state_.type_ == state_indexed;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
typename maybe_const<vit, config::attribute_value>::type& variable_info<vit>::as_scalar() const
|
maybe_const_t<config::attribute_value, V>& variable_info<V>::as_scalar() const
|
||||||
{
|
{
|
||||||
throw_on_invalid();
|
throw_on_invalid();
|
||||||
return apply_visitor(as_skalar_visitor<vit>(), this->state_);
|
return apply_visitor<as_scalar_visitor<V>>(state_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
typename maybe_const<vit, config>::type& variable_info<vit>::as_container() const
|
maybe_const_t<config, V>& variable_info<V>::as_container() const
|
||||||
{
|
{
|
||||||
throw_on_invalid();
|
throw_on_invalid();
|
||||||
return apply_visitor(as_container_visitor<vit>(), this->state_);
|
return apply_visitor<as_container_visitor<V>>(state_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
typename maybe_const<vit, config::child_itors>::type variable_info<vit>::as_array() const
|
maybe_const_t<config::child_itors, V> variable_info<V>::as_array() const
|
||||||
{
|
{
|
||||||
throw_on_invalid();
|
throw_on_invalid();
|
||||||
return apply_visitor(as_array_visitor<vit>(), this->state_);
|
return apply_visitor<as_array_visitor<V>>(state_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
void variable_info<vit>::throw_on_invalid() const
|
void variable_info<V>::throw_on_invalid() const
|
||||||
{
|
{
|
||||||
if(!this->valid_)
|
if(!valid_) {
|
||||||
{
|
|
||||||
throw invalid_variablename_exception();
|
throw invalid_variablename_exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<typename V>
|
||||||
std::string variable_info<vit_const>::get_error_message() const
|
std::string variable_info<V>::get_error_message() const
|
||||||
{
|
{
|
||||||
return "Cannot resolve variablename '" + this->name_ + "' for reading.";
|
return V::error_message(name_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<typename V>
|
||||||
std::string variable_info<vit_create_if_not_existent>::get_error_message() const
|
bool variable_info<V>::exists_as_attribute() const
|
||||||
{
|
{
|
||||||
return "Cannot resolve variablename '" + this->name_ + "' for writing.";
|
throw_on_invalid();
|
||||||
|
return (state_.type_ == state_temporary)
|
||||||
|
|| ((state_.type_ == state_named) && state_.child_->has_attribute(state_.key_));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<typename V>
|
||||||
std::string variable_info<vit_throw_if_not_existent>::get_error_message() const
|
bool variable_info<V>::exists_as_container() const
|
||||||
{
|
{
|
||||||
return "Cannot resolve variablename '" + this->name_ + "' for writing without creating new childs.";
|
throw_on_invalid();
|
||||||
|
return apply_visitor<exists_as_container_visitor<V>>(state_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
void non_const_variable_info<vit>::clear(bool only_tables) const
|
void variable_info_mutable<V>::clear(bool only_tables) const
|
||||||
{
|
{
|
||||||
this->throw_on_invalid();
|
this->throw_on_invalid();
|
||||||
return apply_visitor(clear_value_visitor<vit>(only_tables), this->state_);
|
return apply_visitor<clear_value_visitor<V>>(this->state_, only_tables);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
/**
|
||||||
config::child_itors non_const_variable_info<vit>::append_array(std::vector<config> childs) const
|
* In order to allow the appropriate 'children' argument to forward through to the appropriate
|
||||||
|
* ctor of type T in as_range_visitor_base via apply_visitor, we need to specify the argument
|
||||||
|
* type as an rvalue reference to lvalue reference in order to collapse to an lvalue reference.
|
||||||
|
*
|
||||||
|
* Using a convenient template alias for convenience.
|
||||||
|
*/
|
||||||
|
template<typename V, typename T>
|
||||||
|
using range_visitor_wrapper = as_range_visitor_base<V, T, std::vector<config>&>;
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
config::child_itors variable_info_mutable<V>::append_array(std::vector<config> children) const
|
||||||
{
|
{
|
||||||
this->throw_on_invalid();
|
this->throw_on_invalid();
|
||||||
return apply_visitor(as_range_visitor_base<vit,append_range_h>(append_range_h(childs)), this->state_);
|
return apply_visitor<range_visitor_wrapper<V, append_range_h>>(this->state_, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
config::child_itors non_const_variable_info<vit>::insert_array(std::vector<config> childs) const
|
config::child_itors variable_info_mutable<V>::insert_array(std::vector<config> children) const
|
||||||
{
|
{
|
||||||
this->throw_on_invalid();
|
this->throw_on_invalid();
|
||||||
return apply_visitor(as_range_visitor_base<vit,insert_range_h>(insert_range_h(childs)), this->state_);
|
return apply_visitor<range_visitor_wrapper<V, insert_range_h>>(this->state_, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
config::child_itors non_const_variable_info<vit>::replace_array(std::vector<config> childs) const
|
config::child_itors variable_info_mutable<V>::replace_array(std::vector<config> children) const
|
||||||
{
|
{
|
||||||
this->throw_on_invalid();
|
this->throw_on_invalid();
|
||||||
return apply_visitor(as_range_visitor_base<vit,replace_range_h>(replace_range_h(childs)), this->state_);
|
return apply_visitor<range_visitor_wrapper<V, replace_range_h>>(this->state_, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
template<typename V>
|
||||||
void non_const_variable_info<vit>::merge_array(std::vector<config> childs) const
|
void variable_info_mutable<V>::merge_array(std::vector<config> children) const
|
||||||
{
|
{
|
||||||
this->throw_on_invalid();
|
this->throw_on_invalid();
|
||||||
apply_visitor(as_range_visitor_base<vit,merge_range_h>(merge_range_h(childs)), this->state_);
|
apply_visitor<range_visitor_wrapper<V, merge_range_h>>(this->state_, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
// Force compilation of the following template instantiations
|
||||||
bool variable_info<vit>::exists_as_attribute() const
|
template class variable_info<const vi_policy_const>;
|
||||||
{
|
template class variable_info<vi_policy_create>;
|
||||||
this->throw_on_invalid();
|
template class variable_info<vi_policy_throw>;
|
||||||
return (this->state_.type_ == state_temporary) || ((this->state_.type_ == state_named) && this->state_.child_->has_attribute(this->state_.key_));
|
|
||||||
}
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
bool variable_info<vit>::exists_as_container() const
|
|
||||||
{
|
|
||||||
this->throw_on_invalid();
|
|
||||||
return apply_visitor(exists_as_container_visitor<vit>(), this->state_);
|
|
||||||
}
|
|
||||||
|
|
||||||
///explicit instantiations
|
template class variable_info_mutable<vi_policy_create>;
|
||||||
template class variable_info<vit_const>;
|
template class variable_info_mutable<vi_policy_throw>;
|
||||||
template class variable_info<vit_create_if_not_existent>;
|
|
||||||
template class variable_info<vit_throw_if_not_existent>;
|
|
||||||
template class non_const_variable_info<vit_create_if_not_existent>;
|
|
||||||
template class non_const_variable_info<vit_throw_if_not_existent>;
|
|
||||||
|
|
|
@ -16,98 +16,136 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/** Information on a WML variable. */
|
|
||||||
#include <string>
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "variable_info_detail.hpp"
|
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
class invalid_variablename_exception : public std::exception
|
class invalid_variablename_exception : public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
invalid_variablename_exception() : std::exception() {}
|
invalid_variablename_exception() : std::exception() {}
|
||||||
|
|
||||||
const char* what() const NOEXCEPT
|
const char* what() const NOEXCEPT
|
||||||
{
|
{
|
||||||
return "invalid_variablename_exception";
|
return "invalid_variablename_exception";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<const variable_info_detail::variable_info_type vit>
|
// NOTE: the detail file needs invalid_variablename_exception to be available,
|
||||||
|
// so include this after declaring it.
|
||||||
|
#include "variable_info_detail.hpp"
|
||||||
|
|
||||||
|
/** Information on a WML variable. */
|
||||||
|
template<typename V>
|
||||||
class variable_info
|
class variable_info
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
variable_info(const std::string& varname, maybe_const_t<config, V>& vars) NOEXCEPT;
|
||||||
|
|
||||||
typedef typename variable_info_detail::maybe_const<vit,config>::type config_var;
|
|
||||||
/// Doesn't throw
|
|
||||||
variable_info(const std::string& varname, config_var& vars);
|
|
||||||
~variable_info();
|
|
||||||
std::string get_error_message() const;
|
std::string get_error_message() const;
|
||||||
/// Doesn't throw
|
|
||||||
bool explicit_index() const;
|
bool explicit_index() const NOEXCEPT;
|
||||||
/// might throw invalid_variablename_exception
|
|
||||||
|
/** @throws invalid_variablename_exception */
|
||||||
bool exists_as_attribute() const;
|
bool exists_as_attribute() const;
|
||||||
/// might throw invalid_variablename_exception
|
|
||||||
|
/** @throws invalid_variablename_exception */
|
||||||
bool exists_as_container() const;
|
bool exists_as_container() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
might throw invalid_variablename_exception
|
* If instantiated with vi_policy_const, the lifetime of the returned
|
||||||
NOTE:
|
* const attribute_value reference might end with the lifetime of this object.
|
||||||
If vit == vit_const, then the lifime of the returned const attribute_value& might end with the lifetime of this object.
|
*
|
||||||
*/
|
* @throws invalid_variablename_exception
|
||||||
typename variable_info_detail::maybe_const<vit, config::attribute_value>::type &as_scalar() const;
|
*/
|
||||||
/// might throw invalid_variablename_exception
|
maybe_const_t<config::attribute_value, V>& as_scalar() const;
|
||||||
typename variable_info_detail::maybe_const<vit, config>::type & as_container() const;
|
|
||||||
/// might throw invalid_variablename_exception
|
/**
|
||||||
typename variable_info_detail::maybe_const<vit, config::child_itors>::type as_array() const; //range may be empty
|
* If instantiated with vi_policy_const, the lifetime of the returned
|
||||||
|
* const attribute_value reference might end with the lifetime of this object.
|
||||||
|
*
|
||||||
|
* @throws invalid_variablename_exception
|
||||||
|
*/
|
||||||
|
maybe_const_t<config, V>& as_container() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If instantiated with vi_policy_const, the lifetime of the returned
|
||||||
|
* const attribute_value reference might end with the lifetime of this object.
|
||||||
|
*
|
||||||
|
* Range may be empty
|
||||||
|
* @throws invalid_variablename_exception
|
||||||
|
*/
|
||||||
|
maybe_const_t<config::child_itors, V> as_array() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
variable_info_detail::variable_info_state<vit> state_;
|
variable_info_implementation::variable_info_state<V> state_;
|
||||||
void throw_on_invalid() const;
|
|
||||||
bool valid_;
|
bool valid_;
|
||||||
|
|
||||||
|
void throw_on_invalid() const;
|
||||||
void calculate_value();
|
void calculate_value();
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extends variable_info with methods that can only be applied if vit != vit_const
|
/**
|
||||||
template<const variable_info_detail::variable_info_type vit>
|
* Additional functionality for a non-const variable_info.
|
||||||
class non_const_variable_info : public variable_info<vit>, variable_info_detail::enable_if_non_const<vit>::type
|
* @todo: should these functions take a reference?
|
||||||
|
*/
|
||||||
|
template<typename V>
|
||||||
|
class variable_info_mutable : public variable_info<V>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
non_const_variable_info(const std::string& name, config& game_vars) : variable_info<vit>(name, game_vars) {}
|
variable_info_mutable(const std::string& name, config& game_vars)
|
||||||
~non_const_variable_info() {}
|
: variable_info<V>(name, game_vars)
|
||||||
|
{
|
||||||
|
static_assert(!std::is_same<
|
||||||
|
variable_info_implementation::vi_policy_const, typename std::remove_const<V>::type>::value,
|
||||||
|
"variable_info_mutable cannot be specialized with 'vi_policy_const'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// clears the vale this object points to
|
/**
|
||||||
/// if only_tables = true it will not clear attribute values.
|
* @returns The new appended range.
|
||||||
/// might throw invalid_variablename_exception
|
* @throws invalid_variablename_exception
|
||||||
|
*/
|
||||||
|
config::child_itors append_array(std::vector<config> children) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The new inserted range.
|
||||||
|
* @throws invalid_variablename_exception
|
||||||
|
*/
|
||||||
|
config::child_itors insert_array(std::vector<config> children) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The new range.
|
||||||
|
* @throws invalid_variablename_exception
|
||||||
|
*/
|
||||||
|
config::child_itors replace_array(std::vector<config> children) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws invalid_variablename_exception
|
||||||
|
*/
|
||||||
|
void merge_array(std::vector<config> children) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the value this object points to.
|
||||||
|
*
|
||||||
|
* @param only_tables If true, will not clear attribute values.
|
||||||
|
* @throws invalid_variablename_exception
|
||||||
|
*/
|
||||||
void clear(bool only_tables = false) const;
|
void clear(bool only_tables = false) const;
|
||||||
|
|
||||||
// the following 4 functions are used by [set_variables]
|
|
||||||
// they destroy the passed vector. (make it empty).
|
|
||||||
|
|
||||||
/// @return: the new appended range
|
|
||||||
/// might throw invalid_variablename_exception
|
|
||||||
config::child_itors append_array(std::vector<config> childs) const;
|
|
||||||
/// @return: the new inserted range
|
|
||||||
/// might throw invalid_variablename_exception
|
|
||||||
config::child_itors insert_array(std::vector<config> childs) const;
|
|
||||||
/// @return: the new range
|
|
||||||
/// might throw invalid_variablename_exception
|
|
||||||
config::child_itors replace_array(std::vector<config> childs) const;
|
|
||||||
/// merges
|
|
||||||
/// might throw invalid_variablename_exception
|
|
||||||
void merge_array(std::vector<config> childs) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 'Create if nonexistent' access. */
|
||||||
|
using variable_access_create = variable_info_mutable<variable_info_implementation::vi_policy_create>;
|
||||||
|
|
||||||
|
/** 'Throw if nonexistent' access. */
|
||||||
|
using variable_access_throw = variable_info_mutable<variable_info_implementation::vi_policy_throw>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
this variable accessor will create a childtable when resolving name if it doesn't exist yet.
|
* Read-only access.
|
||||||
*/
|
*
|
||||||
typedef non_const_variable_info<variable_info_detail::vit_create_if_not_existent> variable_access_create;
|
* NOTE: in order to easily mark certain types in this specialization as const we specify
|
||||||
/**
|
* the policy as const here. This allows the use of const_clone.
|
||||||
this variable accessor will throw an exception when trying to access a non existent table.
|
*/
|
||||||
Note that the other types can throw too if name is invlid like '..[[[a'.
|
using variable_access_const = variable_info<const variable_info_implementation::vi_policy_const>;
|
||||||
*/
|
|
||||||
typedef non_const_variable_info<variable_info_detail::vit_throw_if_not_existent> variable_access_throw;
|
|
||||||
/**
|
|
||||||
this variable accessor is takes a const reference and is guaranteed to not change the config.
|
|
||||||
*/
|
|
||||||
typedef variable_info<variable_info_detail::vit_const> variable_access_const;
|
|
||||||
|
|
|
@ -16,80 +16,142 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "utils/const_clone.hpp"
|
||||||
|
|
||||||
namespace variable_info_detail
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace variable_info_implementation
|
||||||
{
|
{
|
||||||
enum variable_info_type {vit_const, vit_create_if_not_existent, vit_throw_if_not_existent, };
|
/**
|
||||||
enum variable_info_state_type {
|
* The variable_info policy classes.
|
||||||
state_start = 0, // for internal use
|
*
|
||||||
// only used at the 'starting_pos' of the variable_info::calculate_value algorithm
|
* Each of these classes describes a different behavior for reading data from a variable
|
||||||
state_named, // the result of .someval this can eigher man an attribute value or an
|
* and should implement two functions:
|
||||||
// child range
|
*
|
||||||
state_indexed, // the result of .someval[index] this is never an attribute value,
|
* - get_child_at Describes the desired behavior when reading variable info.
|
||||||
// this is always a single config.
|
* - error_message Error message regarding policy behavior.
|
||||||
state_temporary, // the result of .length this value can never be written, it can only be read.
|
*/
|
||||||
|
|
||||||
};
|
/** Takes a const reference and is guaranteed to not change the config. */
|
||||||
|
class vi_policy_const
|
||||||
//Special case of std::enable_if
|
{
|
||||||
template<const variable_info_type vit>
|
public:
|
||||||
struct enable_if_non_const
|
static const config& get_child_at(const config& cfg, const std::string& key, int index)
|
||||||
{
|
{
|
||||||
typedef enable_if_non_const<vit> type;
|
assert(index >= 0);
|
||||||
};
|
// cfg.child_or_empty does not support index parameter
|
||||||
|
if(const config& child = cfg.child(key, index)) {
|
||||||
template<>
|
return child;
|
||||||
struct enable_if_non_const<vit_const>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<const variable_info_type vit, typename T>
|
|
||||||
struct maybe_const
|
|
||||||
{
|
|
||||||
typedef T type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
struct maybe_const<vit_const, T>
|
|
||||||
{
|
|
||||||
typedef const T type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct maybe_const<vit_const, config::child_itors>
|
|
||||||
{
|
|
||||||
typedef config::const_child_itors type;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<const variable_info_type vit>
|
|
||||||
struct variable_info_state
|
|
||||||
{
|
|
||||||
typedef typename maybe_const<vit,config>::type child_t;
|
|
||||||
|
|
||||||
variable_info_state(child_t& vars)
|
|
||||||
: child_(&vars)
|
|
||||||
, key_()
|
|
||||||
, index_(0)
|
|
||||||
, temp_val_()
|
|
||||||
, type_(state_start)
|
|
||||||
{
|
|
||||||
child_ = &vars;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The meaning of the following 3 depends on 'type_', but usualy the case is:
|
static const config empty_const_cfg;
|
||||||
// the current config is child_->child_at(key_, index_).
|
return empty_const_cfg;
|
||||||
child_t* child_;
|
}
|
||||||
std::string key_;
|
|
||||||
int index_;
|
|
||||||
|
|
||||||
// If we have a temporary value like .length
|
static std::string error_message(const std::string& name)
|
||||||
// Then we store the result here.
|
{
|
||||||
config::attribute_value temp_val_;
|
return "Cannot resolve variable '" + name + "' for reading.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// See the definition of 'variable_info_state_type'
|
/** Creates a child table when resolving name if it doesn't exist yet. */
|
||||||
variable_info_state_type type_;
|
class vi_policy_create
|
||||||
};
|
{
|
||||||
}
|
public:
|
||||||
|
static config& get_child_at(config& cfg, const std::string& key, int index)
|
||||||
|
{
|
||||||
|
assert(index >= 0);
|
||||||
|
// the 'create_if_not_existent' logic.
|
||||||
|
while(static_cast<int>(cfg.child_count(key)) <= index) {
|
||||||
|
cfg.add_child(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.child(key, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string error_message(const std::string& name)
|
||||||
|
{
|
||||||
|
return "Cannot resolve variable '" + name + "' for writing.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will throw an exception when trying to access a nonexistent table.
|
||||||
|
* Note that the other types can throw too if name is invlid, such as '..[[[a'.
|
||||||
|
*/
|
||||||
|
class vi_policy_throw
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static config& get_child_at(config& cfg, const std::string& key, int index)
|
||||||
|
{
|
||||||
|
assert(index >= 0);
|
||||||
|
if(config& child = cfg.child(key, index)) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw invalid_variablename_exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string error_message(const std::string& name)
|
||||||
|
{
|
||||||
|
return "Cannot resolve variable '" + name + "' for writing without creating new children.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// Other implementation details.
|
||||||
|
// ==================================================================
|
||||||
|
|
||||||
|
template<typename T, typename V>
|
||||||
|
struct maybe_const : public utils::const_clone<T, V>
|
||||||
|
{
|
||||||
|
// Meta type aliases provided by const_clone
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct maybe_const<config::child_itors, const vi_policy_const>
|
||||||
|
{
|
||||||
|
using type = config::const_child_itors;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum variable_info_state_type {
|
||||||
|
state_start = 0, /**< Represents the initial variable state before processing. */
|
||||||
|
state_named, /**< The result of .someval. This can either mean an attribute value or a child range. */
|
||||||
|
state_indexed, /**< The result of .someval[index]. This is never an attribute value and is always a single config. */
|
||||||
|
state_temporary, /**< The result of .length. This value can never be written, it can only be read. */
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
struct variable_info_state
|
||||||
|
{
|
||||||
|
using child_t = typename maybe_const<config, V>::type;
|
||||||
|
|
||||||
|
variable_info_state(child_t& vars)
|
||||||
|
: child_(&vars)
|
||||||
|
, key_()
|
||||||
|
, index_(0)
|
||||||
|
, temp_val_()
|
||||||
|
, type_(state_start)
|
||||||
|
{
|
||||||
|
child_ = &vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The meaning of the following 3 depends on 'type_', but the current config is usually
|
||||||
|
// child_->child_at(key_, index_).
|
||||||
|
child_t* child_;
|
||||||
|
std::string key_;
|
||||||
|
int index_;
|
||||||
|
|
||||||
|
// If we have a temporary value like .length we store the result here.
|
||||||
|
config::attribute_value temp_val_;
|
||||||
|
|
||||||
|
// See @ref variable_info_state_type
|
||||||
|
variable_info_state_type type_;
|
||||||
|
};
|
||||||
|
} // end namespace variable_info_implementation
|
||||||
|
|
||||||
|
/** Helper template alias for maybe_const, defined at global scope for convenience. */
|
||||||
|
template<typename T, typename V>
|
||||||
|
using maybe_const_t = typename variable_info_implementation::maybe_const<T, V>::type;
|
||||||
|
|
496
src/variable_info_private.hpp
Normal file
496
src/variable_info_private.hpp
Normal file
|
@ -0,0 +1,496 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||||
|
Copyright (C) 2005 - 2017 by Philippe Plantier <ayin@anathas.org>
|
||||||
|
|
||||||
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config_assign.hpp"
|
||||||
|
#include "game_config.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace variable_info_implementation
|
||||||
|
{
|
||||||
|
// ==================================================================
|
||||||
|
// General helper functions
|
||||||
|
// ==================================================================
|
||||||
|
|
||||||
|
// TConfig is either 'config' or 'const config'
|
||||||
|
template<typename TConfig>
|
||||||
|
auto get_child_range(TConfig& cfg, const std::string& key, int start, int count) -> decltype(cfg.child_range(key))
|
||||||
|
{
|
||||||
|
auto res = cfg.child_range(key);
|
||||||
|
return {res.begin() + start, res.begin() + start + count};
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolve_negative_value(int size, int& val)
|
||||||
|
{
|
||||||
|
if(val < 0) {
|
||||||
|
val = size + val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// val is still < 0? We don't accept!
|
||||||
|
if(val < 0) {
|
||||||
|
throw invalid_variablename_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const config non_empty_const_cfg = config_of("_", config());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a ']' terminated string.
|
||||||
|
* This is a important optimization of lexical_cast_default
|
||||||
|
*/
|
||||||
|
int parse_index(const char* index_str)
|
||||||
|
{
|
||||||
|
char* endptr;
|
||||||
|
int res = strtol(index_str, &endptr, 10);
|
||||||
|
|
||||||
|
if(*endptr != ']' || res > int(game_config::max_loop) || endptr == index_str) {
|
||||||
|
throw invalid_variablename_exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// Visitor interface
|
||||||
|
// ==================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visitor base class.
|
||||||
|
*
|
||||||
|
* This provides the interface for the main functions each visitor can implement. The default implementation of
|
||||||
|
* each function simply throws @ref invalid_variablename_exception.
|
||||||
|
*
|
||||||
|
* This class also provides two type aliases corresponding to the function return value and argument types.
|
||||||
|
* Note that visitors shouldn't inherit from this directly and instead use the @ref info_visitor and
|
||||||
|
* @ref info_visitor_const wrappers, since both fully specify the parameter type.
|
||||||
|
*
|
||||||
|
* @tparam R Return value type.
|
||||||
|
* @tparam P Argument type.
|
||||||
|
*/
|
||||||
|
template<typename R, typename P>
|
||||||
|
class info_visitor_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using result_t = R;
|
||||||
|
using param_t = P&;
|
||||||
|
|
||||||
|
#define DEFAULTHANDLER(name) \
|
||||||
|
result_t name(param_t) const \
|
||||||
|
{ \
|
||||||
|
throw invalid_variablename_exception(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For use if the variable name was previously empty. This can only happen during calculate_value. */
|
||||||
|
DEFAULTHANDLER(from_start)
|
||||||
|
|
||||||
|
/** For use if the variable ended with a .somename. */
|
||||||
|
DEFAULTHANDLER(from_named)
|
||||||
|
|
||||||
|
/** For use if the variable ended with .somename[someindex]. */
|
||||||
|
DEFAULTHANDLER(from_indexed)
|
||||||
|
|
||||||
|
/** For use if the variable is a readonly value (.somename.length). */
|
||||||
|
DEFAULTHANDLER(from_temporary)
|
||||||
|
|
||||||
|
#undef DEFAULTHANDLER
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename V, typename TResult>
|
||||||
|
using info_visitor = info_visitor_base<TResult, variable_info_state<V>>;
|
||||||
|
|
||||||
|
template<typename V, typename TResult>
|
||||||
|
using info_visitor_const = info_visitor_base<TResult, const variable_info_state<V>>;
|
||||||
|
|
||||||
|
/** Adds a '.<key>' to the current variable. */
|
||||||
|
template<typename V>
|
||||||
|
class get_variable_key_visitor : public info_visitor<V, void>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Import typedefs from base class.
|
||||||
|
using param_t = typename get_variable_key_visitor::param_t;
|
||||||
|
|
||||||
|
get_variable_key_visitor(const std::string& key)
|
||||||
|
: key_(key)
|
||||||
|
{
|
||||||
|
if(!config::valid_id(key_)) {
|
||||||
|
throw invalid_variablename_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_named(param_t state) const
|
||||||
|
{
|
||||||
|
if(key_ == "length") {
|
||||||
|
state.temp_val_ = state.child_->child_count(state.key_);
|
||||||
|
state.type_ = state_temporary;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return do_from_config(V::get_child_at(*state.child_, state.key_, 0), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_start(param_t state) const
|
||||||
|
{
|
||||||
|
return do_from_config(*state.child_, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_indexed(param_t state) const
|
||||||
|
{
|
||||||
|
// We do not support aaa[0].length
|
||||||
|
return do_from_config(V::get_child_at(*state.child_, state.key_, state.index_), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void do_from_config(maybe_const_t<config, V>& cfg, param_t state) const
|
||||||
|
{
|
||||||
|
state.type_ = state_named;
|
||||||
|
state.key_ = key_;
|
||||||
|
state.child_ = &cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends an [index] to the variable.
|
||||||
|
* We only support from_named since [index][index2] or a.length[index] both don't make sense.
|
||||||
|
*/
|
||||||
|
template<typename V>
|
||||||
|
class get_variable_index_visitor : public info_visitor<V, void>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
get_variable_index_visitor(int n)
|
||||||
|
: n_(n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_named(typename get_variable_index_visitor::param_t state) const
|
||||||
|
{
|
||||||
|
state.index_ = n_;
|
||||||
|
resolve_negative_value(state.child_->child_count(state.key_), state.index_);
|
||||||
|
state.type_ = state_indexed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int n_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Tries to convert it to an (maybe const) attribute value. */
|
||||||
|
template<typename V>
|
||||||
|
class as_scalar_visitor : public info_visitor_const<V, maybe_const_t<config::attribute_value, V>&>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Import typedefs from base class.
|
||||||
|
using result_t = typename as_scalar_visitor::result_t;
|
||||||
|
using param_t = typename as_scalar_visitor::param_t;
|
||||||
|
|
||||||
|
result_t from_named(param_t state) const
|
||||||
|
{
|
||||||
|
return (*state.child_)[state.key_];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only implemented for read-only variable_info. Other types use the default throw implementation.
|
||||||
|
*/
|
||||||
|
result_t from_temporary(param_t /*state*/) const
|
||||||
|
{
|
||||||
|
throw invalid_variablename_exception();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Values like '.length' are readonly so we only support reading them, especially since we don't
|
||||||
|
* want to return non-const references.
|
||||||
|
*/
|
||||||
|
template<>
|
||||||
|
const config::attribute_value& as_scalar_visitor<const vi_policy_const>::from_temporary(
|
||||||
|
as_scalar_visitor::param_t state) const
|
||||||
|
{
|
||||||
|
return state.temp_val_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to convert to a [const] config&. Unlike range based operation this also supports 'from_start'.
|
||||||
|
* NOTE: Currently getting the 'from_start' case here is impossible, because we always apply at least one string key.
|
||||||
|
*/
|
||||||
|
template<typename V>
|
||||||
|
class as_container_visitor : public info_visitor_const<V, maybe_const_t<config, V>&>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Import typedefs from base class.
|
||||||
|
using result_t = typename as_container_visitor::result_t;
|
||||||
|
using param_t = typename as_container_visitor::param_t;
|
||||||
|
|
||||||
|
result_t from_named(param_t state) const
|
||||||
|
{
|
||||||
|
return V::get_child_at(*state.child_, state.key_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t from_start(param_t state) const
|
||||||
|
{
|
||||||
|
return *state.child_;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t from_indexed(param_t state) const
|
||||||
|
{
|
||||||
|
return V::get_child_at(*state.child_, state.key_, state.index_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This currently isn't implemented as a range-based operation because doing it on something like range
|
||||||
|
* 2-5 on vi_policy_const if child_ has only 4 elements would be too hard to implement.
|
||||||
|
*/
|
||||||
|
template<typename V>
|
||||||
|
class as_array_visitor : public info_visitor_const<V, maybe_const_t<config::child_itors, V>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Import typedefs from base class.
|
||||||
|
using result_t = typename as_array_visitor::result_t;
|
||||||
|
using param_t = typename as_array_visitor::param_t;
|
||||||
|
|
||||||
|
result_t from_named(param_t state) const
|
||||||
|
{
|
||||||
|
return get_child_range(*state.child_, state.key_, 0, state.child_->child_count(state.key_));
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t from_indexed(param_t state) const
|
||||||
|
{
|
||||||
|
// Ensure we have a config at the given explicit position.
|
||||||
|
V::get_child_at(*state.child_, state.key_, state.index_);
|
||||||
|
return get_child_range(*state.child_, state.key_, state.index_, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
config::const_child_itors as_array_visitor<const vi_policy_const>::from_indexed(as_array_visitor::param_t state) const
|
||||||
|
{
|
||||||
|
if(int(state.child_->child_count(state.key_)) <= state.index_) {
|
||||||
|
return get_child_range(non_empty_const_cfg, "_", 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_child_range(*state.child_, state.key_, state.index_, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam THandler Handler type. Should implement an operator() with the signature:
|
||||||
|
* '(config&, const std::string&, int, int) -> THandler::result_t'
|
||||||
|
*
|
||||||
|
* That does the actual work on the range of children of cfg with name 'name'.
|
||||||
|
* Note this is currently only used by the insert/append/replace/merge operations, so V is always
|
||||||
|
* vi_policy_create.
|
||||||
|
*/
|
||||||
|
template<typename V, typename THandler, typename... T>
|
||||||
|
class as_range_visitor_base : public info_visitor_const<V, typename THandler::result_t>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Import typedefs from base class.
|
||||||
|
using result_t = typename as_range_visitor_base::result_t;
|
||||||
|
using param_t = typename as_range_visitor_base::param_t;
|
||||||
|
|
||||||
|
as_range_visitor_base(T&&... args)
|
||||||
|
: handler_(std::forward<T>(args)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t from_named(param_t state) const
|
||||||
|
{
|
||||||
|
return handler_(*state.child_, state.key_, 0, state.child_->child_count(state.key_));
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t from_indexed(param_t state) const
|
||||||
|
{
|
||||||
|
return handler_(*state.child_, state.key_, state.index_, state.index_ + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
THandler handler_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
class clear_value_visitor : public info_visitor_const<V, void>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Import typedefs from base class.
|
||||||
|
using param_t = typename clear_value_visitor::param_t;
|
||||||
|
|
||||||
|
clear_value_visitor(bool only_tables)
|
||||||
|
: only_tables_(only_tables)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_named(param_t state) const
|
||||||
|
{
|
||||||
|
if(!only_tables_) {
|
||||||
|
state.child_->remove_attribute(state.key_);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.child_->clear_children(state.key_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_indexed(param_t state) const
|
||||||
|
{
|
||||||
|
state.child_->remove_child(state.key_, state.index_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool only_tables_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
class exists_as_container_visitor : public info_visitor_const<V, bool>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Import typedefs from base class.
|
||||||
|
using param_t = typename exists_as_container_visitor::param_t;
|
||||||
|
|
||||||
|
bool from_named(param_t state) const
|
||||||
|
{
|
||||||
|
return state.child_->has_child(state.key_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_indexed(param_t state) const
|
||||||
|
{
|
||||||
|
return state.child_->child_count(state.key_) > static_cast<size_t>(state.index_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_start(param_t) const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_temporary(param_t) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// Range manipulation interface
|
||||||
|
// ==================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the child in [startindex, endindex) with 'source'
|
||||||
|
* 'insert' and 'append' are subcases of this.
|
||||||
|
*/
|
||||||
|
class replace_range_h
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef config::child_itors result_t;
|
||||||
|
replace_range_h(std::vector<config>& source)
|
||||||
|
: datasource_(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t operator()(config& child, const std::string& key, int startindex, int endindex) const
|
||||||
|
{
|
||||||
|
assert(endindex - startindex >= 0);
|
||||||
|
if(endindex > 0) {
|
||||||
|
// NOTE: currently this is only called from as_range_visitor_base<vi_policy_create>
|
||||||
|
// Based on that assumption we use vi_policy_create::get_child_at here instead of making this
|
||||||
|
// a class template.
|
||||||
|
vi_policy_create::get_child_at(child, key, endindex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size_diff = datasource_.size() - (endindex - startindex);
|
||||||
|
|
||||||
|
// remove configs first
|
||||||
|
while(size_diff < 0) {
|
||||||
|
child.remove_child(key, startindex);
|
||||||
|
++size_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for(index = 0; index < static_cast<size_t>(size_diff); ++index) {
|
||||||
|
child.add_child_at(key, config(), startindex + index).swap(datasource_[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; index < datasource_.size(); ++index) {
|
||||||
|
child.child(key, startindex + index).swap(datasource_[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_child_range(child, key, startindex, datasource_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<config>& datasource_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class insert_range_h : replace_range_h
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef config::child_itors result_t;
|
||||||
|
insert_range_h(std::vector<config>& source)
|
||||||
|
: replace_range_h(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t operator()(config& child, const std::string& key, int startindex, int /*endindex*/) const
|
||||||
|
{
|
||||||
|
// insert == replace empty range with data.
|
||||||
|
return replace_range_h::operator()(child, key, startindex, startindex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class append_range_h : insert_range_h
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef config::child_itors result_t;
|
||||||
|
append_range_h(std::vector<config>& source)
|
||||||
|
: insert_range_h(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t operator()(config& child, const std::string& key, int /*startindex*/, int /*endindex*/) const
|
||||||
|
{
|
||||||
|
// append == insert at end.
|
||||||
|
int insert_pos = child.child_count(key);
|
||||||
|
return insert_range_h::operator()(child, key, insert_pos, insert_pos /*ignored by insert_range_h*/);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class merge_range_h
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef void result_t;
|
||||||
|
merge_range_h(std::vector<config>& source)
|
||||||
|
: datasource_(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(config& child, const std::string& key, int startindex, int /*endindex*/) const
|
||||||
|
{
|
||||||
|
// The merge_with function only accepts configs so we convert vector -> config.
|
||||||
|
config datatemp;
|
||||||
|
|
||||||
|
// Add emtpy config to 'shift' the merge to startindex
|
||||||
|
for(int index = 0; index < startindex; ++index) {
|
||||||
|
datatemp.add_child(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move datasource_ -> datatemp
|
||||||
|
for(size_t index = 0; index < datasource_.size(); ++index) {
|
||||||
|
datatemp.add_child(key).swap(datasource_[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
child.merge_with(datatemp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<config>& datasource_;
|
||||||
|
};
|
||||||
|
} // end namespace variable_info_implementation
|
Loading…
Add table
Reference in a new issue