summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnand Thakker <github@anandthakker.net>2017-08-15 14:02:18 -0400
committerAnand Thakker <github@anandthakker.net>2017-10-13 12:50:50 -0400
commitd26c30152a4fbe7510d64430504b85d8b5ab4a1d (patch)
tree103414764c24728742aa60a5c5f89b82645d2682
parent4ec5cabcbb2ecdde73b9b278b0503d7b74e6f3a6 (diff)
downloadqtlocation-mapboxgl-d26c30152a4fbe7510d64430504b85d8b5ab4a1d.tar.gz
Use doubles for intermediate values, check overflow
-rw-r--r--include/mbgl/style/expression/curve.hpp41
-rw-r--r--include/mbgl/style/expression/expression.hpp4
-rw-r--r--include/mbgl/style/expression/literal.hpp61
-rw-r--r--include/mbgl/style/expression/match.hpp57
-rw-r--r--include/mbgl/style/expression/parse.hpp2
-rw-r--r--include/mbgl/style/expression/value.hpp15
-rw-r--r--include/mbgl/util/interpolate.hpp8
-rw-r--r--platform/node/src/node_expression.cpp9
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp50
-rw-r--r--src/mbgl/style/expression/value.cpp94
10 files changed, 241 insertions, 100 deletions
diff --git a/include/mbgl/style/expression/curve.hpp b/include/mbgl/style/expression/curve.hpp
index 11cff10239..9b7a6b64f5 100644
--- a/include/mbgl/style/expression/curve.hpp
+++ b/include/mbgl/style/expression/curve.hpp
@@ -50,11 +50,11 @@ public:
float interpolationFactor(const Range<float>&, const float&) const {
return 0;
}
- float interpolate(const Range<Value>&, const float&) const {
+ EvaluationResult interpolate(const Range<Value>&, const float&) const {
// Assume that Curve::evaluate() will always short circuit due to
// interpolationFactor always returning 0.
assert(false);
- return 0.0f;
+ return Null;
}
};
@@ -207,15 +207,46 @@ struct ParseCurve {
double previous = - std::numeric_limits<double>::infinity();
for (std::size_t i = 3; i + 1 < length; i += 2) {
- const auto& label = toDouble(arrayMember(value, i));
+ const optional<mbgl::Value>& labelValue = toValue(arrayMember(value, i));
+ optional<double> label;
+ optional<std::string> labelError;
+ if (labelValue) {
+ labelValue->match(
+ [&](uint64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](int64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](double n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](const auto&) {}
+ );
+ }
if (!label) {
- ctx.error(R"(Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.)");
+ ctx.error(labelError ? *labelError :
+ R"(Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.)",
+ i);
return ParseResult();
}
if (*label < previous) {
ctx.error(
- R"(Input/output pairs for "curve" expressions must be arranged with input values in strictly ascending order.)"
+ R"(Input/output pairs for "curve" expressions must be arranged with input values in strictly ascending order.)",
+ i
);
return ParseResult();
}
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
index 0e303f0580..1a3666767b 100644
--- a/include/mbgl/style/expression/expression.hpp
+++ b/include/mbgl/style/expression/expression.hpp
@@ -69,8 +69,8 @@ public:
struct EvaluationResult : public Result<Value> {
using Result::Result;
- EvaluationResult(const std::array<float, 4>& arr) :
- Result(std::vector<Value>(arr.begin(), arr.end()))
+ EvaluationResult(const std::array<double, 4>& arr) :
+ Result(toExpressionValue(arr))
{}
};
diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp
index 7a3e4ba0cc..6a6b62103b 100644
--- a/include/mbgl/style/expression/literal.hpp
+++ b/include/mbgl/style/expression/literal.hpp
@@ -28,52 +28,87 @@ public:
template <class V>
static ParseResult parse(const V& value, ParsingContext ctx) {
- const Value& parsedValue = parseValue(value);
+ const optional<Value>& parsedValue = parseValue(value, ctx);
+
+ if (!parsedValue) {
+ return ParseResult();
+ }
// special case: infer the item type if possible for zero-length arrays
if (
ctx.expected &&
ctx.expected->template is<type::Array>() &&
- parsedValue.template is<std::vector<Value>>()
+ parsedValue->template is<std::vector<Value>>()
) {
- auto type = typeOf(parsedValue).template get<type::Array>();
+ auto type = typeOf(*parsedValue).template get<type::Array>();
auto expected = ctx.expected->template get<type::Array>();
if (
type.N && (*type.N == 0) &&
(!expected.N || (*expected.N == 0))
) {
- return ParseResult(std::make_unique<Literal>(expected, parsedValue.template get<std::vector<Value>>()));
+ return ParseResult(std::make_unique<Literal>(expected, parsedValue->template get<std::vector<Value>>()));
}
}
- return ParseResult(std::make_unique<Literal>(parsedValue));
+ return ParseResult(std::make_unique<Literal>(*parsedValue));
}
private:
template <class V>
- static Value parseValue(const V& value) {
+ static optional<Value> parseValue(const V& value, ParsingContext ctx) {
using namespace mbgl::style::conversion;
- if (isUndefined(value)) return Null;
+ if (isUndefined(value)) return {Null};
if (isObject(value)) {
std::unordered_map<std::string, Value> result;
+ bool error = false;
eachMember(value, [&] (const std::string& k, const V& v) -> optional<conversion::Error> {
- result.emplace(k, parseValue(v));
+ if (!error) {
+ optional<Value> memberValue = parseValue(v, ctx);
+ if (memberValue) {
+ result.emplace(k, *memberValue);
+ } else {
+ error = true;
+ }
+ }
return {};
});
- return result;
+ return error ? optional<Value>() : optional<Value>(result);
}
+
if (isArray(value)) {
std::vector<Value> result;
const auto length = arrayLength(value);
for(std::size_t i = 0; i < length; i++) {
- result.emplace_back(parseValue(arrayMember(value, i)));
+ optional<Value> item = parseValue(arrayMember(value, i), ctx);
+ if (item) {
+ result.emplace_back(*item);
+ } else {
+ return optional<Value>();
+ }
}
- return result;
+ return optional<Value>(result);
}
optional<mbgl::Value> v = toValue(value);
assert(v);
-
- return Value(toExpressionValue(*v));
+
+ return v->match(
+ [&](uint64_t n) { return checkNumber(n, ctx); },
+ [&](int64_t n) { return checkNumber(n, ctx); },
+ [&](double n) { return checkNumber(n, ctx); },
+ [&](const auto&) {
+ return optional<Value>(toExpressionValue(*v));
+ }
+ );
+ }
+
+ template <typename T>
+ static optional<Value> checkNumber(T n, ParsingContext ctx) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ return optional<Value>();
+ } else {
+ return {static_cast<double>(n)};
+ }
}
Value value;
diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp
index 9c96f0ae0e..54a3cec9a1 100644
--- a/include/mbgl/style/expression/match.hpp
+++ b/include/mbgl/style/expression/match.hpp
@@ -153,15 +153,49 @@ private:
optional<type::Type> type;
auto value = toValue(input);
- if (value && isIntegerValue(*value)) {
- type = {type::Number};
- result = {*numericValue<int64_t>(*value)};
- } else if (value && value->template is<std::string>()) {
- type = {type::String};
- result = {value->template get<std::string>()};
+
+ if (value) {
+ value->match(
+ [&] (uint64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (int64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ } else {
+ type = {type::Number};
+ result = {n};
+ }
+ },
+ [&] (double n) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ } else if (n != ceilf(n)) {
+ ctx.error("Numeric branch labels must be integer values.");
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (const std::string& s) {
+ type = {type::String};
+ result = {s};
+ },
+ [&] (const auto&) {
+ ctx.error("Branch labels must be numbers or strings.");
+ }
+ );
} else {
ctx.error("Branch labels must be numbers or strings.");
- return optional<InputType>();
+ }
+
+ if (!type) {
+ return result;
}
if (!inputType) {
@@ -204,15 +238,6 @@ private:
std::move(otherwise)
));
}
-
- static bool isIntegerValue(const mbgl::Value& v) {
- return v.match(
- [] (uint64_t) { return true; },
- [] (int64_t) { return true; },
- [] (double t) { return t == ceilf(t); },
- [] (const auto&) { return false; }
- );
- }
};
} // namespace expression
diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp
index 1fa1f5efa4..79a3276227 100644
--- a/include/mbgl/style/expression/parse.hpp
+++ b/include/mbgl/style/expression/parse.hpp
@@ -69,7 +69,7 @@ ParseResult parseExpression(const V& value, ParsingContext context)
return ParseResult();
}
- parsed = Literal::parse(arrayMember(value, 1), ParsingContext(context, 1, context.expected));
+ parsed = Literal::parse(arrayMember(value, 1), context);
} else if (*op == "match") {
parsed = ParseMatch::parse(value, context);
} else if (*op == "curve") {
diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp
index 8a9cafcec1..5bb893059c 100644
--- a/include/mbgl/style/expression/value.hpp
+++ b/include/mbgl/style/expression/value.hpp
@@ -19,14 +19,25 @@ struct Value;
using ValueBase = variant<
NullValue,
bool,
- // TODO: should we break up the numeric types here like mbgl::Value does?
- float,
+ double,
std::string,
mbgl::Color,
mapbox::util::recursive_wrapper<std::vector<Value>>,
mapbox::util::recursive_wrapper<std::unordered_map<std::string, Value>>>;
struct Value : ValueBase {
using ValueBase::ValueBase;
+
+ // Javascript's Number.MAX_SAFE_INTEGER
+ static uint64_t max() { return 9007199254740991ULL; }
+
+ static bool isSafeNumericValue(uint64_t x) { return x <= max(); };
+ static bool isSafeNumericValue(int64_t x) {
+ return static_cast<uint64_t>(x > 0 ? x : -x) <= max();
+ }
+ static bool isSafeNumericValue(double x) {
+ return static_cast<uint64_t>(x > 0 ? x : -x) <= max();
+ }
+
};
Value toExpressionValue(const Value&);
diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp
index c5e04c459f..aff730a0a2 100644
--- a/include/mbgl/util/interpolate.hpp
+++ b/include/mbgl/util/interpolate.hpp
@@ -66,11 +66,11 @@ struct Interpolator<std::vector<style::expression::Value>> {
if (a.size() == 0) return {};
std::vector<style::expression::Value> result;
for (std::size_t i = 0; i < a.size(); i++) {
- assert(a[i].template is<float>());
- assert(b[i].template is<float>());
+ assert(a[i].template is<double>());
+ assert(b[i].template is<double>());
style::expression::Value item = interpolate(
- a[i].template get<float>(),
- b[i].template get<float>(),
+ a[i].template get<double>(),
+ b[i].template get<double>(),
t);
result.push_back(item);
}
diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp
index c72fc26702..65a9439ddb 100644
--- a/platform/node/src/node_expression.cpp
+++ b/platform/node/src/node_expression.cpp
@@ -121,7 +121,7 @@ struct ToValue {
return scope.Escape(Nan::New(t));
}
- v8::Local<v8::Value> operator()(float t) {
+ v8::Local<v8::Value> operator()(double t) {
Nan::EscapableHandleScope scope;
return scope.Escape(Nan::New(t));
}
@@ -141,7 +141,12 @@ struct ToValue {
}
v8::Local<v8::Value> operator()(const mbgl::Color& color) {
- return operator()(std::vector<Value> { color.r, color.g, color.b, color.a });
+ return operator()(std::vector<Value> {
+ static_cast<double>(color.r),
+ static_cast<double>(color.g),
+ static_cast<double>(color.b),
+ static_cast<double>(color.a)
+ });
}
v8::Local<v8::Value> operator()(const std::unordered_map<std::string, Value>& map) {
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
index e7f3fe773c..97d1860fa3 100644
--- a/src/mbgl/style/expression/compound_expression.cpp
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -133,7 +133,7 @@ static Definition defineGet() {
// Define "zoom" expression, it being the only one for which isZoomConstant == false.
static std::pair<std::string, Definition> defineZoom() {
- auto zoom = [](const EvaluationParameters& params) -> Result<float> {
+ auto zoom = [](const EvaluationParameters& params) -> Result<double> {
if (!params.zoom) {
return EvaluationError {
"The 'zoom' expression is unavailable in the current evaluation context."
@@ -157,13 +157,13 @@ Result<T> assertion(const Value& v) {
return v.get<T>();
}
-std::string stringifyColor(float r, float g, float b, float a) {
+std::string stringifyColor(double r, double g, double b, double a) {
return stringify(r) + ", " +
stringify(g) + ", " +
stringify(b) + ", " +
stringify(a);
}
-Result<mbgl::Color> rgba(float r, float g, float b, float a) {
+Result<mbgl::Color> rgba(double r, double g, double b, double a) {
if (
r < 0 || r > 255 ||
g < 0 || g > 255 ||
@@ -191,27 +191,27 @@ std::unordered_map<std::string, CompoundExpressions::Definition> initializeDefin
}
std::unordered_map<std::string, Definition> CompoundExpressions::definitions = initializeDefinitions(
- define("e", []() -> Result<float> { return 2.718281828459045f; }),
- define("pi", []() -> Result<float> { return 3.141592653589793f; }),
- define("ln2", []() -> Result<float> { return 0.6931471805599453; }),
+ define("e", []() -> Result<double> { return 2.718281828459045f; }),
+ define("pi", []() -> Result<double> { return 3.141592653589793f; }),
+ define("ln2", []() -> Result<double> { return 0.6931471805599453; }),
define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); }),
- define("number", assertion<float>),
+ define("number", assertion<double>),
define("string", assertion<std::string>),
define("boolean", assertion<bool>),
define("to_string", [](const Value& v) -> Result<std::string> { return stringify(v); }),
- define("to_number", [](const Value& v) -> Result<float> {
- optional<float> result = v.match(
- [](const float f) -> optional<float> { return f; },
- [](const std::string& s) -> optional<float> {
+ define("to_number", [](const Value& v) -> Result<double> {
+ optional<double> result = v.match(
+ [](const double f) -> optional<double> { return f; },
+ [](const std::string& s) -> optional<double> {
try {
return std::stof(s);
} catch(std::exception) {
- return optional<float>();
+ return optional<double>();
}
},
- [](const auto&) { return optional<float>(); }
+ [](const auto&) { return optional<double>(); }
);
if (!result) {
return EvaluationError {
@@ -222,15 +222,15 @@ std::unordered_map<std::string, Definition> CompoundExpressions::definitions = i
}),
define("to_boolean", [](const Value& v) -> Result<bool> {
return v.match(
- [&] (float f) { return (bool)f; },
+ [&] (double f) { return (bool)f; },
[&] (const std::string& s) { return s.length() > 0; },
[&] (bool b) { return b; },
[&] (const NullValue&) { return false; },
[&] (const auto&) { return true; }
);
}),
- define("to_rgba", [](const mbgl::Color& color) -> Result<std::array<float, 4>> {
- return std::array<float, 4> {{ color.r, color.g, color.b, color.a }};
+ define("to_rgba", [](const mbgl::Color& color) -> Result<std::array<double, 4>> {
+ return std::array<double, 4> {{ color.r, color.g, color.b, color.a }};
}),
define("parse_color", [](const std::string& colorString) -> Result<mbgl::Color> {
@@ -242,16 +242,16 @@ std::unordered_map<std::string, Definition> CompoundExpressions::definitions = i
}),
define("rgba", rgba),
- define("rgb", [](float r, float g, float b) { return rgba(r, g, b, 1.0f); }),
+ define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); }),
defineZoom(),
std::pair<std::string, Definition>("has", defineHas()),
std::pair<std::string, Definition>("get", defineGet()),
- define("length", [](const std::vector<Value>& arr) -> Result<float> {
+ define("length", [](const std::vector<Value>& arr) -> Result<double> {
return arr.size();
- }, [] (const std::string s) -> Result<float> {
+ }, [] (const std::string s) -> Result<double> {
return s.size();
}),
@@ -308,22 +308,22 @@ std::unordered_map<std::string, Definition> CompoundExpressions::definitions = i
);
}),
- define("+", [](const Varargs<float>& args) -> Result<float> {
- float sum = 0.0f;
+ define("+", [](const Varargs<double>& args) -> Result<double> {
+ double sum = 0.0f;
for (auto arg : args) {
sum += arg;
}
return sum;
}),
- define("-", [](float a, float b) -> Result<float> { return a - b; }),
- define("*", [](const Varargs<float>& args) -> Result<float> {
- float prod = 1.0f;
+ define("-", [](double a, double b) -> Result<double> { return a - b; }),
+ define("*", [](const Varargs<double>& args) -> Result<double> {
+ double prod = 1.0f;
for (auto arg : args) {
prod *= arg;
}
return prod;
}),
- define("/", [](float a, float b) -> Result<float> { return a / b; }),
+ define("/", [](double a, double b) -> Result<double> { return a / b; }),
define("&&", [](const Varargs<bool>& args) -> Result<bool> {
bool result = true;
diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp
index c6d6bc2a9a..cbcf7e2206 100644
--- a/src/mbgl/style/expression/value.cpp
+++ b/src/mbgl/style/expression/value.cpp
@@ -8,7 +8,7 @@ namespace expression {
type::Type typeOf(const Value& value) {
return value.match(
[&](bool) -> type::Type { return type::Boolean; },
- [&](float) -> type::Type { return type::Number; },
+ [&](double) -> type::Type { return type::Number; },
[&](const std::string&) -> type::Type { return type::String; },
[&](const mbgl::Color&) -> type::Type { return type::Color; },
[&](const NullValue&) -> type::Type { return type::Null; },
@@ -39,7 +39,7 @@ std::string stringify(const Value& value) {
return value.match(
[&] (const NullValue&) { return std::string("null"); },
[&] (bool b) { return std::string(b ? "true" : "false"); },
- [&] (float f) {
+ [&] (double f) {
std::stringstream ss;
ss << f;
return ss.str();
@@ -76,6 +76,27 @@ struct Converter {
}
};
+template <>
+struct Converter<float> {
+ static Value toExpressionValue(const float& value) {
+ return static_cast<double>(value);
+ }
+
+ static optional<float> fromExpressionValue(const Value& value) {
+ if (value.template is<double>()) {
+ double v = value.template get<double>();
+ if (v <= Value::max()) {
+ return static_cast<float>(v);
+ }
+ }
+ return optional<float>();
+ }
+
+ static type::Type expressionType() {
+ return type::Number;
+ }
+};
+
template<>
struct Converter<mbgl::Value> {
static Value toExpressionValue(const mbgl::Value& value) {
@@ -103,33 +124,50 @@ struct Converter<mbgl::Value> {
Value operator()(const std::string& s) { return s; }
Value operator()(const bool& b) { return b; }
Value operator()(const mbgl::NullValue) { return Null; }
-
- template <typename T>
- Value operator()(const T& v) { return *numericValue<float>(v); }
+ Value operator()(const double& v) { return v; }
+ Value operator()(const uint64_t& v) {
+ return v > static_cast<uint64_t>(Value::max()) ? static_cast<double>(Value::max()) : v;
+ }
+ Value operator()(const int64_t& v) {
+ return v > std::numeric_limits<double>::max() ? std::numeric_limits<double>::max() : v;
+ }
};
+template <typename T, typename Container>
+std::vector<Value> toArrayValue(const Container& value) {
+ std::vector<Value> result;
+ for (const T& item : value) {
+ result.push_back(Converter<T>::toExpressionValue(item));
+ }
+ return result;
+}
+
+template <typename T, typename Container>
+optional<Container> fromArrayValue(const std::vector<Value>& value) {
+ Container result;
+ auto it = result.begin();
+ for(const Value& item : value) {
+ optional<T> convertedItem = Converter<T>::fromExpressionValue(item);
+ if (!convertedItem) {
+ return optional<Container>();
+ }
+ *it = *convertedItem;
+ it = std::next(it);
+ }
+ return result;
+}
+
template <typename T, std::size_t N>
struct Converter<std::array<T, N>> {
static Value toExpressionValue(const std::array<T, N>& value) {
- std::vector<Value> result(N);
- std::copy_n(value.begin(), N, result.begin());
- return result;
+ return toArrayValue<T>(value);
}
static optional<std::array<T, N>> fromExpressionValue(const Value& value) {
return value.match(
[&] (const std::vector<Value>& v) -> optional<std::array<T, N>> {
if (v.size() != N) return optional<std::array<T, N>>();
- std::array<T, N> result;
- auto it = result.begin();
- for(const auto& item : v) {
- if (!item.template is<T>()) {
- return optional<std::array<T, N>>();
- }
- *it = item.template get<T>();
- it = std::next(it);
- }
- return result;
+ return fromArrayValue<T, std::array<T, N>>(v);
},
[&] (const auto&) { return optional<std::array<T, N>>(); }
);
@@ -143,22 +181,13 @@ struct Converter<std::array<T, N>> {
template <typename T>
struct Converter<std::vector<T>> {
static Value toExpressionValue(const std::vector<T>& value) {
- std::vector<Value> v(value.size());
- std::copy(value.begin(), value.end(), v.begin());
- return v;
+ return toArrayValue<T>(value);
}
static optional<std::vector<T>> fromExpressionValue(const Value& value) {
return value.match(
[&] (const std::vector<Value>& v) -> optional<std::vector<T>> {
- std::vector<T> result;
- for(const auto& item : v) {
- if (!item.template is<T>()) {
- return optional<std::vector<T>>();
- }
- result.push_back(item.template get<T>());
- }
- return result;
+ return fromArrayValue<T, std::vector<T>>(v);
},
[&] (const auto&) { return optional<std::vector<T>>(); }
);
@@ -227,7 +256,7 @@ type::Type valueTypeToExpressionType() {
template <> type::Type valueTypeToExpressionType<Value>() { return type::Value; }
template <> type::Type valueTypeToExpressionType<NullValue>() { return type::Null; }
template <> type::Type valueTypeToExpressionType<bool>() { return type::Boolean; }
-template <> type::Type valueTypeToExpressionType<float>() { return type::Number; }
+template <> type::Type valueTypeToExpressionType<double>() { return type::Number; }
template <> type::Type valueTypeToExpressionType<std::string>() { return type::String; }
template <> type::Type valueTypeToExpressionType<mbgl::Color>() { return type::Color; }
template <> type::Type valueTypeToExpressionType<std::unordered_map<std::string, Value>>() { return type::Object; }
@@ -245,6 +274,11 @@ struct instantiate {
}
};
+// for to_rgba expression
+template struct instantiate<std::array<double, 4>>;
+
+// layout/paint property types
+template struct instantiate<float>;
template struct instantiate<std::array<float, 2>>;
template struct instantiate<std::array<float, 4>>;
template struct instantiate<std::vector<float>>;