move config::attribte_value out of config.?pp

this has 2 advantages:
1) we can now forwaed declare the class config_attribute_value without
including config.hpp
2) the config.?pp files are a little easier to read.
This commit is contained in:
gfgtdf 2017-04-29 20:23:47 +02:00
parent d06e9c30b2
commit 13b00f73ff
5 changed files with 643 additions and 498 deletions

View file

@ -1,6 +1,7 @@
color.cpp
color_range.cpp
config.cpp
config_attribute_value.cpp
filesystem_boost.cpp
filesystem_common.cpp
font/constants.cpp

View file

@ -107,340 +107,6 @@ struct config_implementation
};
/* ** Attribute value implementation ** */
// Special string values.
const std::string config::attribute_value::s_yes("yes");
const std::string config::attribute_value::s_no("no");
const std::string config::attribute_value::s_true("true");
const std::string config::attribute_value::s_false("false");
/** Default implementation, but defined out-of-line for efficiency reasons. */
config::attribute_value::attribute_value()
: value_()
{
}
/** Default implementation, but defined out-of-line for efficiency reasons. */
config::attribute_value::~attribute_value()
{
}
/** Default implementation, but defined out-of-line for efficiency reasons. */
config::attribute_value::attribute_value(const config::attribute_value &that)
: value_(that.value_)
{
}
/** Default implementation, but defined out-of-line for efficiency reasons. */
config::attribute_value &config::attribute_value::operator=(const config::attribute_value &that)
{
value_ = that.value_;
return *this;
}
config::attribute_value &config::attribute_value::operator=(bool v)
{
value_ = yes_no(v);
return *this;
}
config::attribute_value &config::attribute_value::operator=(int v)
{
value_ = v;
return *this;
}
config::attribute_value &config::attribute_value::operator=(long long v)
{
if ( v > 0 )
// We can store this unsigned.
return *this = static_cast<unsigned long long>(v);
if ( v >= INT_MIN )
// We can store this as an int.
return *this = static_cast<int>(v);
// Getting to this point should be rare. (Currently, getting here means
// something like there was so much draining in a campaign that the
// total damage taken is not only negative, but so negative that an
// int cannot hold the value.) So rare that it is not worth precise
// treatment; just use a double.
value_ = static_cast<double>(v);
return *this;
}
config::attribute_value &config::attribute_value::operator=(unsigned long long v)
{
// Use int for smaller numbers.
if ( v <= INT_MAX )
return *this = static_cast<int>(v);
value_ = v;
return *this;
}
config::attribute_value &config::attribute_value::operator=(double v)
{
// Try to store integers in other types.
if ( v > 0.0 ) {
// Convert to unsigned and pass this off to that assignment operator.
unsigned long long ull = static_cast<unsigned long long>(v);
if ( static_cast<double>(ull) == v )
return *this = ull;
}
else {
// Convert to integer and pass this off to that assignment operator.
int i = static_cast<int>(v);
if ( static_cast<double>(i) == v )
return *this = i;
}
// If we get here, this does in fact get stored as a double.
value_ = v;
return *this;
}
namespace {
/**
* Attempts to convert @a source to the template type.
* This is to avoid "overzealous reinterpretations of certain WML strings as
* numeric types" (c.f. bug #19201).
* @returns true if the conversion was successful and the source string
* can be reobtained by streaming the result.
*/
template<typename To>
bool from_string_verify(const std::string & source, To & res)
{
// Check 1: convertable to the target type.
std::istringstream in_str(source);
if ( !(in_str >> res) )
return false;
// Check 2: convertable back to the same string.
std::ostringstream out_str;
out_str << res;
return out_str.str() == source;
}
}
config::attribute_value &config::attribute_value::operator=(const std::string &v)
{
// Handle some special strings.
if (v.empty()) { value_ = v; return *this; }
if ( v == s_yes ) { value_ = yes_no(true); return *this; }
if ( v == s_no ) { value_ = yes_no(false); return *this; }
if ( v == s_true ) { value_ = true_false(true); return *this; }
if ( v == s_false ) { value_ = true_false(false); return *this; }
// Attempt to convert to a number.
char *eptr;
double d = strtod(v.c_str(), &eptr);
if ( *eptr == '\0' ) {
// Possibly a number. See what type it should be stored in.
// (All conversions will be from the string since the largest integer
// type could have more precision than a double.)
if ( d > 0.0 ) {
// The largest type for positive integers is unsigned long long.
unsigned long long ull = 0;
if ( from_string_verify<unsigned long long>(v, ull) )
return *this = ull;
}
else {
// The largest (variant) type for negative integers is int.
int i = 0;
if ( from_string_verify<int>(v, i) )
return *this = i;
}
// This does not look like an integer, so it should be a double.
// However, make sure it can convert back to the same string (in
// case this is a string that just looks like a numeric value).
std::ostringstream tester;
tester << d;
if ( tester.str() == v ) {
value_ = d;
return *this;
}
}
// No conversion possible. Store the string.
value_ = v;
return *this;
}
config::attribute_value &config::attribute_value::operator=(const t_string &v)
{
if (!v.translatable()) return *this = v.str();
value_ = v;
return *this;
}
bool config::attribute_value::to_bool(bool def) const
{
if ( const yes_no *p = boost::get<const yes_no>(&value_) )
return *p;
if ( const true_false *p = boost::get<const true_false>(&value_) )
return *p;
// No other types are ever recognized as boolean.
return def;
}
namespace {
/// Visitor for converting a variant to a numeric type (T).
template <typename T>
class attribute_numeric_visitor : public boost::static_visitor<T>
{
public:
// Constructor stores the default value.
attribute_numeric_visitor(T def) : def_(def) {}
T operator()(boost::blank const &) const { return def_; }
T operator()(bool) const { return def_; }
T operator()(int i) const { return static_cast<T>(i); }
T operator()(unsigned long long u) const { return static_cast<T>(u); }
T operator()(double d) const { return static_cast<T>(d); }
T operator()(const std::string& s) const { return lexical_cast_default<T>(s, def_); }
T operator()(t_string const &) const { return def_; }
private:
const T def_;
};
}
int config::attribute_value::to_int(int def) const
{
return apply_visitor(attribute_numeric_visitor<int>(def));
}
long long config::attribute_value::to_long_long(long long def) const
{
return apply_visitor(attribute_numeric_visitor<long long>(def));
}
unsigned config::attribute_value::to_unsigned(unsigned def) const
{
return apply_visitor(attribute_numeric_visitor<unsigned>(def));
}
size_t config::attribute_value::to_size_t(size_t def) const
{
return apply_visitor(attribute_numeric_visitor<size_t>(def));
}
time_t config::attribute_value::to_time_t(time_t def) const
{
return apply_visitor(attribute_numeric_visitor<time_t>(def));
}
double config::attribute_value::to_double(double def) const
{
return apply_visitor(attribute_numeric_visitor<double>(def));
}
/// Visitor for converting a variant to a string.
class config::attribute_value::string_visitor
: public boost::static_visitor<std::string>
{
const std::string default_;
public:
string_visitor(const std::string& fallback) : default_(fallback) {}
std::string operator()(const boost::blank &) const { return default_; }
std::string operator()(const yes_no & b) const { return b.str(); }
std::string operator()(const true_false & b) const { return b.str(); }
std::string operator()(int i) const { return lexical_cast<std::string>(i); }
std::string operator()(unsigned long long u) const { return lexical_cast<std::string>(u); }
std::string operator()(double d) const { return lexical_cast<std::string>(d); }
std::string operator()(const std::string& s) const { return s; }
std::string operator()(t_string const &s) const { return s.str(); }
};
std::string config::attribute_value::str(const std::string& fallback) const
{
return apply_visitor(string_visitor(fallback));
}
t_string config::attribute_value::t_str() const
{
if (const t_string *p = boost::get<const t_string>(&value_)) return *p;
return str();
}
/**
* Tests for an attribute that was never set.
*/
bool config::attribute_value::blank() const
{
return boost::get<const boost::blank>(&value_) != nullptr;
}
/**
* Tests for an attribute that either was never set or was set to "".
*/
bool config::attribute_value::empty() const
{
if (boost::get<const boost::blank>(&value_)) return true;
if (const std::string *p = boost::get<const std::string>(&value_)) return p->empty();
return false;
}
/// Visitor handling equality checks.
class config::attribute_value::equality_visitor
: public boost::static_visitor<bool>
{
public:
// Most generic: not equal.
template <typename T, typename U>
bool operator()(const T &, const U &) const { return false; }
// Same types are comparable and might be equal.
template <typename T>
bool operator()(const T & lhs, const T & rhs) const { return lhs == rhs; }
// Boolean values can be compared.
bool operator()(const true_false & lhs, const yes_no & rhs) const { return bool(lhs) == bool(rhs); }
bool operator()(const yes_no & lhs, const true_false & rhs) const { return bool(lhs) == bool(rhs); }
};
/**
* Checks for equality of the attribute values when viewed as strings.
* Exception: Boolean synonyms can be equal ("yes" == "true").
* Note: Blanks have no string representation, so do not equal "" (an empty string).
*/
bool config::attribute_value::operator==(const config::attribute_value &other) const
{
return boost::apply_visitor(equality_visitor(), value_, other.value_);
}
/**
* Checks for equality of the attribute values when viewed as strings.
* Exception: Boolean synonyms can be equal ("yes" == "true").
* Note: Blanks have no string representation, so do not equal "" (an empty string).
* Also note that translatable string are never equal to non translatable strings.
*/
bool config::attribute_value::equals(const std::string &str) const
{
attribute_value v;
v = str;
return *this == v;
// if c["a"] = "1" then this solution would have resulted in c["a"] == "1" beeing false
// because a["a"] is '1' and not '"1"'.
// return boost::apply_visitor(std::bind( equality_visitor(), _1, std::cref(str) ), value_);
// that's why we don't use it.
}
std::ostream &operator<<(std::ostream &os, const config::attribute_value &v)
{
// Simple implementation, but defined out-of-line because of the templating
// involved.
return os << v.value_;
}
/* ** config implementation ** */

View file

@ -45,6 +45,7 @@
#include <boost/variant/variant.hpp>
#include <boost/range/iterator_range.hpp>
#include "config_attribute_value.hpp"
#include "exceptions.hpp"
#include "tstring.hpp"
@ -232,168 +233,7 @@ public:
* @note The blank variant is only used when querying missing attributes.
* It is not stored in config objects.
*/
class attribute_value
{
/// A wrapper for bool to get the correct streaming ("true"/"false").
/// Most visitors can simply treat this as bool.
public:
class true_false
{
bool value_;
public:
explicit true_false(bool value = false) : value_(value) {}
operator bool() const { return value_; }
const std::string & str() const
{ return value_ ? config::attribute_value::s_true :
config::attribute_value::s_false; }
};
friend std::ostream& operator<<(std::ostream &os, const true_false &v);
/// A wrapper for bool to get the correct streaming ("yes"/"no").
/// Most visitors can simply treat this as bool.
class yes_no
{
bool value_;
public:
explicit yes_no(bool value = false) : value_(value) {}
operator bool() const { return value_; }
const std::string & str() const
{ return value_ ? config::attribute_value::s_yes :
config::attribute_value::s_no; }
};
friend std::ostream& operator<<(std::ostream &os, const yes_no &v);
private:
/// Visitor for checking equality.
class equality_visitor;
/// Visitor for converting a variant to a string.
class string_visitor;
// Data will be stored in a variant, allowing for the possibility of
// boolean, numeric, and translatable data in addition to basic string
// data. For most purposes, int is the preferred type for numeric data
// as it is fast (often natural word size). While it is desirable to
// use few types (to keep the overhead low), we do have use cases for
// fractions (double) and huge numbers (up to the larger of LLONG_MAX
// and SIZE_MAX).
typedef boost::variant<boost::blank,
true_false, yes_no,
int, unsigned long long, double,
std::string, t_string
> value_type;
/// The stored value will always use the first type from the variant
/// definition that can represent it and that can be streamed to the
/// correct string representation (if applicable).
/// This is enforced upon assignment.
value_type value_;
public:
/// Default implementation, but defined out-of-line for efficiency reasons.
attribute_value();
/// Default implementation, but defined out-of-line for efficiency reasons.
~attribute_value();
/// Default implementation, but defined out-of-line for efficiency reasons.
attribute_value(const attribute_value &);
/// Default implementation, but defined out-of-line for efficiency reasons.
attribute_value &operator=(const attribute_value &);
// Numeric assignments:
attribute_value &operator=(bool v);
attribute_value &operator=(int v);
attribute_value &operator=(long v) { return operator=(static_cast<long long>(v)); }
attribute_value &operator=(long long v);
attribute_value &operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
attribute_value &operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
attribute_value &operator=(unsigned long long v);
attribute_value &operator=(double v);
// String assignments:
attribute_value &operator=(const char *v) { return operator=(std::string(v)); }
attribute_value &operator=(const std::string &v);
attribute_value &operator=(const t_string &v);
template<typename T>
typename std::enable_if<std::is_base_of<enum_tag, T>::value, attribute_value &>::type operator=(const T &v)
{
return operator=(T::enum_to_string(v));
}
// Extracting as a specific type:
bool to_bool(bool def = false) const;
int to_int(int def = 0) const;
long long to_long_long(long long def = 0) const;
unsigned to_unsigned(unsigned def = 0) const;
size_t to_size_t(size_t def = 0) const;
time_t to_time_t(time_t def = 0) const;
double to_double(double def = 0.) const;
std::string str(const std::string& fallback = "") const;
t_string t_str() const;
/**
@param T a type created with MAKE_ENUM macro
NOTE: since T::VALUE constants is not of type T but of the underlying enum type you must specify the template parameter explicitly
TODO: Fix this in c++11 using constexpr types.
*/
template<typename T>
typename std::enable_if<std::is_base_of<enum_tag, T>::value, T>::type to_enum(const T &v) const
{
return T::string_to_enum(this->str(), v);
}
// Implicit conversions:
operator int() const { return to_int(); }
operator std::string() const { return str(); }
operator t_string() const { return t_str(); }
/// Tests for an attribute that was never set.
bool blank() const;
/// Tests for an attribute that either was never set or was set to "".
bool empty() const;
// Comparisons:
bool operator==(const attribute_value &other) const;
bool operator!=(const attribute_value &other) const
{ return !operator==(other); }
bool equals(const std::string& str) const;
// These function prevent t_string creation in case of c["a"] == "b" comparisons.
// The templates are needed to prevent using these function in case of c["a"] == 0 comparisons.
template<typename T>
typename std::enable_if<std::is_same<const std::string, typename std::add_const<T>::type>::value, bool>::type
friend operator==(const attribute_value &val, const T &str)
{ return val.equals(str); }
template<typename T>
typename std::enable_if<std::is_same<const char*, T>::value, bool>::type
friend operator==(const attribute_value &val, T str)
{ return val.equals(std::string(str)); }
template<typename T>
bool friend operator==(const T &str, const attribute_value &val)
{ return val == str; }
template<typename T>
bool friend operator!=(const attribute_value &val, const T &str)
{ return !(val == str); }
template<typename T>
bool friend operator!=(const T &str, const attribute_value &val)
{ return !(val == str); }
// Streaming:
friend std::ostream& operator<<(std::ostream &os, const attribute_value &v);
// Visitor support:
/// Applies a visitor to the underlying variant.
/// (See the documentation for Boost.Variant.)
template <typename V>
typename V::result_type apply_visitor(const V & visitor) const
{ return boost::apply_visitor(visitor, value_); }
private:
// Special strings.
static const std::string s_yes, s_no;
static const std::string s_true, s_false;
};
using attribute_value = config_attribute_value;
typedef std::map<
std::string
@ -911,5 +751,3 @@ public:
virtual config::attribute_value get_variable_const(const std::string &id) const = 0;
};
inline std::ostream &operator<<(std::ostream &os, const config::attribute_value::true_false &v) { return os << v.str(); }
inline std::ostream &operator<<(std::ostream &os, const config::attribute_value::yes_no &v) { return os << v.str(); }

View file

@ -0,0 +1,371 @@
/*
Copyright (C) 2003 by David White <dave@whitevine.net>
Copyright (C) 2005 - 2017 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
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.
*/
/**
* @file
* Routines related to configuration-files / WML.
*/
#include "config_attribute_value.hpp"
#include "lexical_cast.hpp"
#include "log.hpp"
#include "utils/const_clone.hpp"
#include <cstdlib>
#include <cstring>
#include <deque>
#include <istream>
#include "utils/functional.hpp"
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/get.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/variant/variant.hpp>
static lg::log_domain log_config("config");
#define ERR_CF LOG_STREAM(err, log_config)
#define DBG_CF LOG_STREAM(debug, log_config)
// Special string values.
const std::string config_attribute_value::s_yes("yes");
const std::string config_attribute_value::s_no("no");
const std::string config_attribute_value::s_true("true");
const std::string config_attribute_value::s_false("false");
/** Default implementation, but defined out-of-line for efficiency reasons. */
config_attribute_value::config_attribute_value()
: value_()
{
}
/** Default implementation, but defined out-of-line for efficiency reasons. */
config_attribute_value::~config_attribute_value()
{
}
/** Default implementation, but defined out-of-line for efficiency reasons. */
config_attribute_value::config_attribute_value(const config_attribute_value &that)
: value_(that.value_)
{
}
/** Default implementation, but defined out-of-line for efficiency reasons. */
config_attribute_value &config_attribute_value::operator=(const config_attribute_value &that)
{
value_ = that.value_;
return *this;
}
config_attribute_value &config_attribute_value::operator=(bool v)
{
value_ = yes_no(v);
return *this;
}
config_attribute_value &config_attribute_value::operator=(int v)
{
value_ = v;
return *this;
}
config_attribute_value &config_attribute_value::operator=(long long v)
{
if ( v > 0 )
// We can store this unsigned.
return *this = static_cast<unsigned long long>(v);
if ( v >= INT_MIN )
// We can store this as an int.
return *this = static_cast<int>(v);
// Getting to this point should be rare. (Currently, getting here means
// something like there was so much draining in a campaign that the
// total damage taken is not only negative, but so negative that an
// int cannot hold the value.) So rare that it is not worth precise
// treatment; just use a double.
value_ = static_cast<double>(v);
return *this;
}
config_attribute_value &config_attribute_value::operator=(unsigned long long v)
{
// Use int for smaller numbers.
if ( v <= INT_MAX )
return *this = static_cast<int>(v);
value_ = v;
return *this;
}
config_attribute_value &config_attribute_value::operator=(double v)
{
// Try to store integers in other types.
if ( v > 0.0 ) {
// Convert to unsigned and pass this off to that assignment operator.
unsigned long long ull = static_cast<unsigned long long>(v);
if ( static_cast<double>(ull) == v )
return *this = ull;
}
else {
// Convert to integer and pass this off to that assignment operator.
int i = static_cast<int>(v);
if ( static_cast<double>(i) == v )
return *this = i;
}
// If we get here, this does in fact get stored as a double.
value_ = v;
return *this;
}
namespace {
/**
* Attempts to convert @a source to the template type.
* This is to avoid "overzealous reinterpretations of certain WML strings as
* numeric types" (c.f. bug #19201).
* @returns true if the conversion was successful and the source string
* can be reobtained by streaming the result.
*/
template<typename To>
bool from_string_verify(const std::string & source, To & res)
{
// Check 1: convertable to the target type.
std::istringstream in_str(source);
if ( !(in_str >> res) )
return false;
// Check 2: convertable back to the same string.
std::ostringstream out_str;
out_str << res;
return out_str.str() == source;
}
}
config_attribute_value &config_attribute_value::operator=(const std::string &v)
{
// Handle some special strings.
if (v.empty()) { value_ = v; return *this; }
if ( v == s_yes ) { value_ = yes_no(true); return *this; }
if ( v == s_no ) { value_ = yes_no(false); return *this; }
if ( v == s_true ) { value_ = true_false(true); return *this; }
if ( v == s_false ) { value_ = true_false(false); return *this; }
// Attempt to convert to a number.
char *eptr;
double d = strtod(v.c_str(), &eptr);
if ( *eptr == '\0' ) {
// Possibly a number. See what type it should be stored in.
// (All conversions will be from the string since the largest integer
// type could have more precision than a double.)
if ( d > 0.0 ) {
// The largest type for positive integers is unsigned long long.
unsigned long long ull = 0;
if ( from_string_verify<unsigned long long>(v, ull) )
return *this = ull;
}
else {
// The largest (variant) type for negative integers is int.
int i = 0;
if ( from_string_verify<int>(v, i) )
return *this = i;
}
// This does not look like an integer, so it should be a double.
// However, make sure it can convert back to the same string (in
// case this is a string that just looks like a numeric value).
std::ostringstream tester;
tester << d;
if ( tester.str() == v ) {
value_ = d;
return *this;
}
}
// No conversion possible. Store the string.
value_ = v;
return *this;
}
config_attribute_value &config_attribute_value::operator=(const t_string &v)
{
if (!v.translatable()) return *this = v.str();
value_ = v;
return *this;
}
bool config_attribute_value::to_bool(bool def) const
{
if ( const yes_no *p = boost::get<const yes_no>(&value_) )
return *p;
if ( const true_false *p = boost::get<const true_false>(&value_) )
return *p;
// No other types are ever recognized as boolean.
return def;
}
namespace {
/// Visitor for converting a variant to a numeric type (T).
template <typename T>
class attribute_numeric_visitor : public boost::static_visitor<T>
{
public:
// Constructor stores the default value.
attribute_numeric_visitor(T def) : def_(def) {}
T operator()(boost::blank const &) const { return def_; }
T operator()(bool) const { return def_; }
T operator()(int i) const { return static_cast<T>(i); }
T operator()(unsigned long long u) const { return static_cast<T>(u); }
T operator()(double d) const { return static_cast<T>(d); }
T operator()(const std::string& s) const { return lexical_cast_default<T>(s, def_); }
T operator()(t_string const &) const { return def_; }
private:
const T def_;
};
}
int config_attribute_value::to_int(int def) const
{
return apply_visitor(attribute_numeric_visitor<int>(def));
}
long long config_attribute_value::to_long_long(long long def) const
{
return apply_visitor(attribute_numeric_visitor<long long>(def));
}
unsigned config_attribute_value::to_unsigned(unsigned def) const
{
return apply_visitor(attribute_numeric_visitor<unsigned>(def));
}
size_t config_attribute_value::to_size_t(size_t def) const
{
return apply_visitor(attribute_numeric_visitor<size_t>(def));
}
time_t config_attribute_value::to_time_t(time_t def) const
{
return apply_visitor(attribute_numeric_visitor<time_t>(def));
}
double config_attribute_value::to_double(double def) const
{
return apply_visitor(attribute_numeric_visitor<double>(def));
}
/// Visitor for converting a variant to a string.
class config_attribute_value::string_visitor
: public boost::static_visitor<std::string>
{
const std::string default_;
public:
string_visitor(const std::string& fallback) : default_(fallback) {}
std::string operator()(const boost::blank &) const { return default_; }
std::string operator()(const yes_no & b) const { return b.str(); }
std::string operator()(const true_false & b) const { return b.str(); }
std::string operator()(int i) const { return lexical_cast<std::string>(i); }
std::string operator()(unsigned long long u) const { return lexical_cast<std::string>(u); }
std::string operator()(double d) const { return lexical_cast<std::string>(d); }
std::string operator()(const std::string& s) const { return s; }
std::string operator()(t_string const &s) const { return s.str(); }
};
std::string config_attribute_value::str(const std::string& fallback) const
{
return apply_visitor(string_visitor(fallback));
}
t_string config_attribute_value::t_str() const
{
if (const t_string *p = boost::get<const t_string>(&value_)) return *p;
return str();
}
/**
* Tests for an attribute that was never set.
*/
bool config_attribute_value::blank() const
{
return boost::get<const boost::blank>(&value_) != nullptr;
}
/**
* Tests for an attribute that either was never set or was set to "".
*/
bool config_attribute_value::empty() const
{
if (boost::get<const boost::blank>(&value_)) return true;
if (const std::string *p = boost::get<const std::string>(&value_)) return p->empty();
return false;
}
/// Visitor handling equality checks.
class config_attribute_value::equality_visitor
: public boost::static_visitor<bool>
{
public:
// Most generic: not equal.
template <typename T, typename U>
bool operator()(const T &, const U &) const { return false; }
// Same types are comparable and might be equal.
template <typename T>
bool operator()(const T & lhs, const T & rhs) const { return lhs == rhs; }
// Boolean values can be compared.
bool operator()(const true_false & lhs, const yes_no & rhs) const { return bool(lhs) == bool(rhs); }
bool operator()(const yes_no & lhs, const true_false & rhs) const { return bool(lhs) == bool(rhs); }
};
/**
* Checks for equality of the attribute values when viewed as strings.
* Exception: Boolean synonyms can be equal ("yes" == "true").
* Note: Blanks have no string representation, so do not equal "" (an empty string).
*/
bool config_attribute_value::operator==(const config_attribute_value &other) const
{
return boost::apply_visitor(equality_visitor(), value_, other.value_);
}
/**
* Checks for equality of the attribute values when viewed as strings.
* Exception: Boolean synonyms can be equal ("yes" == "true").
* Note: Blanks have no string representation, so do not equal "" (an empty string).
* Also note that translatable string are never equal to non translatable strings.
*/
bool config_attribute_value::equals(const std::string &str) const
{
config_attribute_value v;
v = str;
return *this == v;
// if c["a"] = "1" then this solution would have resulted in c["a"] == "1" beeing false
// because a["a"] is '1' and not '"1"'.
// return boost::apply_visitor(std::bind( equality_visitor(), _1, std::cref(str) ), value_);
// that's why we don't use it.
}
std::ostream &operator<<(std::ostream &os, const config_attribute_value &v)
{
// Simple implementation, but defined out-of-line because of the templating
// involved.
return os << v.value_;
}

View file

@ -0,0 +1,269 @@
/*
Copyright (C) 2003 - 2017 by David White <dave@whitevine.net>
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.
*/
/**
* @file
* Definitions for the interface to Wesnoth Markup Language (WML).
*
* This module defines the interface to Wesnoth Markup Language (WML). WML is
* a simple hierarchical text-based file format. The format is defined in
* Wiki, under BuildingScenariosWML
*
* All configuration files are stored in this format, and data is sent across
* the network in this format. It is thus used extensively throughout the
* game.
*/
#pragma once
#include "global.hpp"
#include <climits>
#include <ctime>
#include <iosfwd>
#include <iterator>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <type_traits>
#include <memory>
#include <boost/exception/exception.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/variant.hpp>
#include <boost/range/iterator_range.hpp>
#include "exceptions.hpp"
#include "tstring.hpp"
#ifdef HAVE_CXX14
# ifdef __clang__ // Check this first, because clang also defines __GNUC__
# ifdef __apple_build_version__ // Apple clang
# if (__clang_major__ == 5 && __clang_minor__ >= 1) || __clang_major__ > 5 // Apple clang 5.1+
# define USE_HETEROGENOUS_LOOKUPS
# endif
# else // Non-Apple clang
# if (__clang_major__ == 3 && __clang_minor__ >= 4) || __clang_major__ > 3 // clang 3.4+
# define USE_HETEROGENOUS_LOOKUPS
# endif
# endif
# elif defined(__GNUC__) && __GNUC__ >= 5 // GCC 5.0+
# define USE_HETEROGENOUS_LOOKUPS
# endif
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1900 // MSVC 2015
# define USE_HETEROGENOUS_LOOKUPS
#endif
#ifdef USE_HETEROGENOUS_LOOKUPS
#if BOOST_VERSION > 106100
#include <boost/utility/string_view.hpp>
using config_key_type = boost::string_view;
#else
#include <boost/utility/string_ref.hpp>
using config_key_type = boost::string_ref;
#endif
#else
using config_key_type = const std::string &;
#endif
class enum_tag;
/**
* Variant for storing WML attributes.
* The most efficient type is used when assigning a value. For instance,
* strings "yes", "no", "true", "false" will be detected and stored as boolean.
* @note The blank variant is only used when querying missing attributes.
* It is not stored in config objects.
*/
class config_attribute_value
{
/// A wrapper for bool to get the correct streaming ("true"/"false").
/// Most visitors can simply treat this as bool.
public:
class true_false
{
bool value_;
public:
explicit true_false(bool value = false) : value_(value) {}
operator bool() const { return value_; }
const std::string & str() const
{
return value_ ? config_attribute_value::s_true : config_attribute_value::s_false;
}
};
friend std::ostream& operator<<(std::ostream &os, const true_false &v) { return os << v.str(); }
/// A wrapper for bool to get the correct streaming ("yes"/"no").
/// Most visitors can simply treat this as bool.
class yes_no
{
bool value_;
public:
explicit yes_no(bool value = false) : value_(value) {}
operator bool() const { return value_; }
const std::string & str() const
{
return value_ ? config_attribute_value::s_yes : config_attribute_value::s_no;
}
};
friend std::ostream& operator<<(std::ostream &os, const yes_no &v) { return os << v.str(); }
private:
/// Visitor for checking equality.
class equality_visitor;
/// Visitor for converting a variant to a string.
class string_visitor;
// Data will be stored in a variant, allowing for the possibility of
// boolean, numeric, and translatable data in addition to basic string
// data. For most purposes, int is the preferred type for numeric data
// as it is fast (often natural word size). While it is desirable to
// use few types (to keep the overhead low), we do have use cases for
// fractions (double) and huge numbers (up to the larger of LLONG_MAX
// and SIZE_MAX).
typedef boost::variant<boost::blank,
true_false, yes_no,
int, unsigned long long, double,
std::string, t_string
> value_type;
/// The stored value will always use the first type from the variant
/// definition that can represent it and that can be streamed to the
/// correct string representation (if applicable).
/// This is enforced upon assignment.
value_type value_;
public:
/// Default implementation, but defined out-of-line for efficiency reasons.
config_attribute_value();
/// Default implementation, but defined out-of-line for efficiency reasons.
~config_attribute_value();
/// Default implementation, but defined out-of-line for efficiency reasons.
config_attribute_value(const config_attribute_value &);
/// Default implementation, but defined out-of-line for efficiency reasons.
config_attribute_value &operator=(const config_attribute_value &);
// Numeric assignments:
config_attribute_value& operator=(bool v);
config_attribute_value& operator=(int v);
config_attribute_value& operator=(long v) { return operator=(static_cast<long long>(v)); }
config_attribute_value& operator=(long long v);
config_attribute_value& operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
config_attribute_value& operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
config_attribute_value& operator=(unsigned long long v);
config_attribute_value& operator=(double v);
// String assignments:
config_attribute_value& operator=(const char *v) { return operator=(std::string(v)); }
config_attribute_value& operator=(const std::string &v);
config_attribute_value& operator=(const t_string &v);
template<typename T>
typename std::enable_if<std::is_base_of<enum_tag, T>::value, config_attribute_value &>::type operator=(const T &v)
{
return operator=(T::enum_to_string(v));
}
// Extracting as a specific type:
bool to_bool(bool def = false) const;
int to_int(int def = 0) const;
long long to_long_long(long long def = 0) const;
unsigned to_unsigned(unsigned def = 0) const;
size_t to_size_t(size_t def = 0) const;
time_t to_time_t(time_t def = 0) const;
double to_double(double def = 0.) const;
std::string str(const std::string& fallback = "") const;
t_string t_str() const;
/**
@param T a type created with MAKE_ENUM macro
NOTE: since T::VALUE constants is not of type T but of the underlying enum type you must specify the template parameter explicitly
TODO: Fix this in c++11 using constexpr types.
*/
template<typename T>
typename std::enable_if<std::is_base_of<enum_tag, T>::value, T>::type to_enum(const T &v) const
{
return T::string_to_enum(this->str(), v);
}
// Implicit conversions:
operator int() const { return to_int(); }
operator std::string() const { return str(); }
operator t_string() const { return t_str(); }
/// Tests for an attribute that was never set.
bool blank() const;
/// Tests for an attribute that either was never set or was set to "".
bool empty() const;
// Comparisons:
bool operator==(const config_attribute_value &other) const;
bool operator!=(const config_attribute_value &other) const
{
return !operator==(other);
}
bool equals(const std::string& str) const;
// These function prevent t_string creation in case of c["a"] == "b" comparisons.
// The templates are needed to prevent using these function in case of c["a"] == 0 comparisons.
template<typename T>
typename std::enable_if<std::is_same<const std::string, typename std::add_const<T>::type>::value, bool>::type
friend operator==(const config_attribute_value &val, const T &str)
{
return val.equals(str);
}
template<typename T>
typename std::enable_if<std::is_same<const char*, T>::value, bool>::type
friend operator==(const config_attribute_value& val, T str)
{
return val.equals(std::string(str));
}
template<typename T>
bool friend operator==(const T& str, const config_attribute_value& val)
{
return val == str;
}
template<typename T>
bool friend operator!=(const config_attribute_value& val, const T& str)
{
return !(val == str);
}
template<typename T>
bool friend operator!=(const T &str, const config_attribute_value& val)
{
return !(val == str);
}
// Streaming:
friend std::ostream& operator<<(std::ostream& os, const config_attribute_value& v);
// Visitor support:
/// Applies a visitor to the underlying variant.
/// (See the documentation for Boost.Variant.)
template <typename V>
typename V::result_type apply_visitor(const V & visitor) const
{
return boost::apply_visitor(visitor, value_);
}
private:
// Special strings.
static const std::string s_yes, s_no;
static const std::string s_true, s_false;
};