diff options
Diffstat (limited to 'include/mbgl')
42 files changed, 577 insertions, 687 deletions
diff --git a/include/mbgl/math/log2.hpp b/include/mbgl/math/log2.hpp index 6a1ba23ed9..3136ac22b4 100644 --- a/include/mbgl/math/log2.hpp +++ b/include/mbgl/math/log2.hpp @@ -2,6 +2,11 @@ #include <cmath> #include <cstdint> +#include <type_traits> + +#if defined(__ANDROID__) +#include <android/api-level.h> +#endif namespace mbgl { namespace util { @@ -12,3 +17,14 @@ uint32_t ceil_log2(uint64_t x); } // namespace util } // namespace mbgl + +// log2 is not available on Android before API 18. +#if defined(__ANDROID__) && defined(__GNUC__) && \ + defined(__ANDROID_API__) && __ANDROID_API__ < 18 + +template <typename T> +typename std::enable_if_t<std::is_floating_point<T>::value, T> log2(T x) { + return ::log(x) / M_LN2; +} + +#endif diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index b9c8de5052..e048d82af2 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -5,6 +5,7 @@ #include <mbgl/storage/offline.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/expected.hpp> #include <vector> #include <mutex> @@ -55,8 +56,7 @@ public: * callback, which will be executed on the database thread; it is the responsibility * of the SDK bindings to re-execute a user-provided callback on the main thread. */ - void listOfflineRegions(std::function<void (std::exception_ptr, - optional<std::vector<OfflineRegion>>)>); + void listOfflineRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)>); /* * Create an offline region in the database. @@ -71,16 +71,14 @@ public: */ void createOfflineRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, - optional<OfflineRegion>)>); + std::function<void (expected<OfflineRegion, std::exception_ptr>)>); /* * Update an offline region metadata in the database. */ void updateOfflineMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, - optional<OfflineRegionMetadata>)>); + std::function<void (expected<OfflineRegionMetadata, std::exception_ptr>)>); /* * Register an observer to be notified when the state of the region changes. */ @@ -97,8 +95,9 @@ public: * executed on the database thread; it is the responsibility of the SDK bindings * to re-execute a user-provided callback on the main thread. */ - void getOfflineRegionStatus(OfflineRegion&, std::function<void (std::exception_ptr, - optional<OfflineRegionStatus>)>) const; + void getOfflineRegionStatus( + OfflineRegion&, + std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)>) const; /* * Remove an offline region from the database and perform any resources evictions diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index ef4a499e83..b4e40cb5f3 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -1,8 +1,10 @@ #pragma once #include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> #include <mbgl/util/range.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/variant.hpp> #include <mbgl/style/types.hpp> #include <mbgl/storage/response.hpp> @@ -30,22 +32,40 @@ public: OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float); /* Private */ - std::vector<CanonicalTileID> tileCover(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; - uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; const std::string styleURL; const LatLngBounds bounds; const double minZoom; const double maxZoom; const float pixelRatio; -private: - Range<uint8_t> coveringZoomRange(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; }; /* - * For the present, a tile pyramid is the only type of offline region. In the future, - * other definition types will be available and this will be a variant type. + * An offline region defined by a style URL, geometry, zoom range, and + * device pixel ratio. + * + * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. + * + * maxZoom may be ∞, in which case for each tile source, the region will include + * tiles from minZoom up to the maximum zoom level provided by that source. + * + * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. */ -using OfflineRegionDefinition = OfflineTilePyramidRegionDefinition; +class OfflineGeometryRegionDefinition { +public: + OfflineGeometryRegionDefinition(std::string styleURL, Geometry<double>, double minZoom, double maxZoom, float pixelRatio); + + /* Private */ + const std::string styleURL; + const Geometry<double> geometry; + const double minZoom; + const double maxZoom; + const float pixelRatio; +}; + +/* + * The offline region definition types supported + */ +using OfflineRegionDefinition = variant<OfflineTilePyramidRegionDefinition, OfflineGeometryRegionDefinition>; /* * The encoded format is private. @@ -187,11 +207,11 @@ class OfflineRegion { public: // Move-only; not publicly constructible. OfflineRegion(OfflineRegion&&); - OfflineRegion& operator=(OfflineRegion&&); ~OfflineRegion(); OfflineRegion() = delete; OfflineRegion(const OfflineRegion&) = delete; + OfflineRegion& operator=(OfflineRegion&&) = delete; OfflineRegion& operator=(const OfflineRegion&) = delete; int64_t getID() const; @@ -210,4 +230,6 @@ private: const OfflineRegionMetadata metadata; }; +using OfflineRegions = std::vector<OfflineRegion>; + } // namespace mbgl diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp index 71c2cec237..2c83d1561b 100644 --- a/include/mbgl/style/conversion.hpp +++ b/include/mbgl/style/conversion.hpp @@ -1,306 +1,26 @@ #pragma once -#include <mbgl/util/optional.hpp> -#include <mbgl/util/feature.hpp> -#include <mbgl/util/geojson.hpp> - #include <string> namespace mbgl { namespace style { namespace conversion { -/* - The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by - the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain: - - * `std::unique_ptr<Source>` - * `std::unique_ptr<Layer>` - * `Filter` - * `PropertyValue<T>` - - A single template function serves as the public interface: - - template <class T> - optional<T> convert(const Convertible& input, Error& error); - - Where `T` is one of the above types. If the conversion fails, the result is empty, and the - error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, - a filled optional is returned. - - `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that - can serve as input to the conversion algorithm. For instance, on macOS, we need to support - conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc. - On Qt, we need to support conversion from RapidJSON types and QVariant. - - We don't want to use traditional forms of polymorphism to accomplish this: - - * Compile time polymorphism using a template parameter for the actual value type leads to - excessive code bloat and long compile times. - * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous - use of std::unique_ptr, unsuitable for this performance-sensitive code. - - Therefore, we're using a custom implementation of runtime polymorphism where we manually create and - dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible - underlying types inline on the stack, using `std::aligned_storage`. - - For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This - specialization must provide the following static methods: - - * `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null - - * `isArray(v)` -- returns a boolean indicating whether `v` represents a JSON array - * `arrayLength(v)` -- called only if `isArray(v)`; returns a size_t length - * `arrayMember(v)` -- called only if `isArray(v)`; returns `V` or `V&` - - * `isObject(v)` -- returns a boolean indicating whether `v` represents a JSON object - * `objectMember(v, name)` -- called only if `isObject(v)`; `name` is `const char *`; return value: - * is true when evaluated in a boolean context iff the named member exists - * is convertable to a `V` or `V&` when dereferenced - * `eachMember(v, [] (const std::string&, const V&) -> optional<Error> {...})` -- called - only if `isObject(v)`; calls the provided lambda once for each key and value of the object; - short-circuits if any call returns an `Error` - - * `toBool(v)` -- returns `optional<bool>`, absence indicating `v` is not a JSON boolean - * `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number - * `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number - * `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string - * `toValue(v)` -- returns `optional<Value>`, a variant type, for generic conversion, - absence indicating `v` is not a boolean, number, or string. Numbers should be converted to - unsigned integer, signed integer, or floating point, in descending preference. - - In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for - `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the - possible underlying types. (A static assert will fail if this is not the case.) - - `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state; - you must not do anything with it except let it go out of scope. -*/ +// This is a forward-declaration only header intended to minimize dependencies and to improve +// compilation speed. In order to specialize implementations and get access to the actual +// implementation, include <mbgl/style/conversion_impl.hpp>. struct Error { std::string message; }; template <typename T> class ConversionTraits; -class Convertible { -public: - template <typename T> - Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) { - static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type"); - new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value)); - } - - Convertible(Convertible&& v) - : vtable(v.vtable) - { - if (vtable) { - vtable->move(std::move(v.storage), this->storage); - } - } - - ~Convertible() { - if (vtable) { - vtable->destroy(storage); - } - } - - Convertible& operator=(Convertible&& v) { - if (vtable) { - vtable->destroy(storage); - } - vtable = v.vtable; - if (vtable) { - vtable->move(std::move(v.storage), this->storage); - } - v.vtable = nullptr; - return *this; - } - - Convertible() = delete; - Convertible(const Convertible&) = delete; - Convertible& operator=(const Convertible&) = delete; - - friend inline bool isUndefined(const Convertible& v) { - assert(v.vtable); - return v.vtable->isUndefined(v.storage); - } - - friend inline bool isArray(const Convertible& v) { - assert(v.vtable); - return v.vtable->isArray(v.storage); - } - - friend inline std::size_t arrayLength(const Convertible& v) { - assert(v.vtable); - return v.vtable->arrayLength(v.storage); - } - - friend inline Convertible arrayMember(const Convertible& v, std::size_t i) { - assert(v.vtable); - return v.vtable->arrayMember(v.storage, i); - } - - friend inline bool isObject(const Convertible& v) { - assert(v.vtable); - return v.vtable->isObject(v.storage); - } - - friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) { - assert(v.vtable); - return v.vtable->objectMember(v.storage, name); - } - - friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) { - assert(v.vtable); - return v.vtable->eachMember(v.storage, fn); - } - - friend inline optional<bool> toBool(const Convertible& v) { - assert(v.vtable); - return v.vtable->toBool(v.storage); - } - - friend inline optional<float> toNumber(const Convertible& v) { - assert(v.vtable); - return v.vtable->toNumber(v.storage); - } - - friend inline optional<double> toDouble(const Convertible& v) { - assert(v.vtable); - return v.vtable->toDouble(v.storage); - } - - friend inline optional<std::string> toString(const Convertible& v) { - assert(v.vtable); - return v.vtable->toString(v.storage); - } - - friend inline optional<Value> toValue(const Convertible& v) { - assert(v.vtable); - return v.vtable->toValue(v.storage); - } - - friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) { - assert(v.vtable); - return v.vtable->toGeoJSON(v.storage, error); - } - -private: -#if __ANDROID__ - // Android: JSValue* or mbgl::android::Value - using Storage = std::aligned_storage_t<32, 8>; -#elif __QT__ - // Qt: JSValue* or QVariant - using Storage = std::aligned_storage_t<32, 8>; -#else - // Node: JSValue* or v8::Local<v8::Value> - // iOS/macOS: JSValue* or id - using Storage = std::aligned_storage_t<8, 8>; -#endif - - struct VTable { - void (*move) (Storage&& src, Storage& dest); - void (*destroy) (Storage&); - - bool (*isUndefined) (const Storage&); - - bool (*isArray) (const Storage&); - std::size_t (*arrayLength) (const Storage&); - Convertible (*arrayMember) (const Storage&, std::size_t); - - bool (*isObject) (const Storage&); - optional<Convertible> (*objectMember) (const Storage&, const char *); - optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&); - - optional<bool> (*toBool) (const Storage&); - optional<float> (*toNumber) (const Storage&); - optional<double> (*toDouble) (const Storage&); - optional<std::string> (*toString) (const Storage&); - optional<Value> (*toValue) (const Storage&); - - // https://github.com/mapbox/mapbox-gl-native/issues/5623 - optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&); - }; - - // Extracted this function from the table below to work around a GCC bug with differing - // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 - template <typename T> - static auto vtableEachMember(const Storage& s, const std::function<optional<Error>(const std::string&, const Convertible&)>& fn) { - return ConversionTraits<T>::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) { - return fn(k, Convertible(std::move(v))); - }); - } - - template <typename T> - static VTable* vtableForType() { - using Traits = ConversionTraits<T>; - static VTable vtable = { - [] (Storage&& src, Storage& dest) { - auto srcValue = reinterpret_cast<T&&>(src); - new (static_cast<void*>(&dest)) T(std::move(srcValue)); - srcValue.~T(); - }, - [] (Storage& s) { - reinterpret_cast<T&>(s).~T(); - }, - [] (const Storage& s) { - return Traits::isUndefined(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::isArray(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::arrayLength(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s, std::size_t i) { - return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i)); - }, - [] (const Storage& s) { - return Traits::isObject(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s, const char * key) { - optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key); - if (member) { - return optional<Convertible>(Convertible(std::move(*member))); - } else { - return optional<Convertible>(); - } - }, - vtableEachMember<T>, - [] (const Storage& s) { - return Traits::toBool(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toNumber(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toDouble(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toString(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toValue(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s, Error& err) { - return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err); - } - }; - return &vtable; - } - - VTable* vtable; - Storage storage; -}; +class Convertible; template <class T, class Enable = void> struct Converter; -template <class T, class...Args> -optional<T> convert(const Convertible& value, Error& error, Args&&...args) { - return Converter<T>()(value, error, std::forward<Args>(args)...); -} - } // namespace conversion } // namespace style } // namespace mbgl + diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp index 7d74ec42ce..40657528c4 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/style/conversion.hpp> +#include <mbgl/style/types.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/enum.hpp> #include <mbgl/util/string.hpp> @@ -30,21 +31,7 @@ struct Converter<std::string> { template <class T> struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { - optional<T> operator()(const Convertible& value, Error& error) const { - optional<std::string> string = toString(value); - if (!string) { - error.message = "value must be a string"; - return nullopt; - } - - const auto result = Enum<T>::toEnum(*string); - if (!result) { - error.message = "value must be a valid enumeration value"; - return nullopt; - } - - return *result; - } + optional<T> operator()(const Convertible& value, Error& error) const; }; template <> @@ -54,23 +41,7 @@ struct Converter<Color> { template <size_t N> struct Converter<std::array<float, N>> { - optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const { - if (!isArray(value) || arrayLength(value) != N) { - error.message = "value must be an array of " + util::toString(N) + " numbers"; - return nullopt; - } - - std::array<float, N> result; - for (size_t i = 0; i < N; i++) { - optional<float> n = toNumber(arrayMember(value, i)); - if (!n) { - error.message = "value must be an array of " + util::toString(N) + " numbers"; - return nullopt; - } - result[i] = *n; - } - return result; - } + optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const; }; template <> diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp index e11db5e32f..1346ed738a 100644 --- a/include/mbgl/style/conversion/coordinate.hpp +++ b/include/mbgl/style/conversion/coordinate.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/custom_geometry_source_options.hpp b/include/mbgl/style/conversion/custom_geometry_source_options.hpp index f0f505e54f..090e5b6239 100644 --- a/include/mbgl/style/conversion/custom_geometry_source_options.hpp +++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp index 9daf6ea7a4..2d7ad0afc7 100644 --- a/include/mbgl/style/conversion/filter.hpp +++ b/include/mbgl/style/conversion/filter.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/filter.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index 49825a3410..ba9acd7a3b 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/property_expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/expression.hpp> #include <mbgl/style/expression/value.hpp> @@ -16,25 +16,7 @@ std::unique_ptr<expression::Expression> convertTokenStringToExpression(const std optional<std::unique_ptr<expression::Expression>> convertFunctionToExpression(expression::type::Type, const Convertible&, Error&, bool convertTokens); template <class T> -optional<PropertyExpression<T>> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens) { - auto expression = convertFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error, convertTokens); - if (!expression) { - return nullopt; - } - - optional<T> defaultValue; - - auto defaultValueValue = objectMember(value, "default"); - if (defaultValueValue) { - defaultValue = convert<T>(*defaultValueValue, error); - if (!defaultValue) { - error.message = R"(wrong type for "default": )" + error.message; - return nullopt; - } - } - - return PropertyExpression<T>(std::move(*expression), defaultValue); -} +optional<PropertyExpression<T>> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens); } // namespace conversion } // namespace style diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp index 403c5f953b..90c1d64197 100644 --- a/include/mbgl/style/conversion/geojson.hpp +++ b/include/mbgl/style/conversion/geojson.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/util/geojson.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 3f625babb6..89511848dd 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/style/sources/geojson_source.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp index 1c0e2e2f07..9cf019378b 100644 --- a/include/mbgl/style/conversion/layer.hpp +++ b/include/mbgl/style/conversion/layer.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> #include <memory> @@ -15,8 +16,6 @@ public: optional<std::unique_ptr<Layer>> operator()(const Convertible& value, Error& error) const; }; -optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value); -optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value); optional<Error> setPaintProperties(Layer& layer, const Convertible& value); } // namespace conversion diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp index 289fca2e31..2f6f8628b8 100644 --- a/include/mbgl/style/conversion/light.hpp +++ b/include/mbgl/style/conversion/light.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/light.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp index 044c45862d..10db5c6ec1 100644 --- a/include/mbgl/style/conversion/position.hpp +++ b/include/mbgl/style/conversion/position.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/style/position.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index fa6752867b..f6f36db983 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -1,9 +1,9 @@ #pragma once #include <mbgl/style/property_value.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/function.hpp> +#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/value.hpp> #include <mbgl/style/expression/is_constant.hpp> #include <mbgl/style/expression/is_expression.hpp> @@ -16,53 +16,7 @@ namespace conversion { template <class T> struct Converter<PropertyValue<T>> { - optional<PropertyValue<T>> operator()(const Convertible& value, Error& error, bool allowDataExpressions, bool convertTokens) const { - using namespace mbgl::style::expression; - - if (isUndefined(value)) { - return PropertyValue<T>(); - } - - optional<PropertyExpression<T>> expression; - - if (isExpression(value)) { - ParsingContext ctx(valueTypeToExpressionType<T>()); - ParseResult parsed = ctx.parseLayerPropertyExpression(value); - if (!parsed) { - error.message = ctx.getCombinedErrors(); - return nullopt; - } - expression = PropertyExpression<T>(std::move(*parsed)); - } else if (isObject(value)) { - expression = convertFunctionToExpression<T>(value, error, convertTokens); - } else { - optional<T> constant = convert<T>(value, error); - if (!constant) { - return nullopt; - } - return convertTokens ? maybeConvertTokens(*constant) : PropertyValue<T>(*constant); - } - - if (!expression) { - return nullopt; - } else if (!allowDataExpressions && !(*expression).isFeatureConstant()) { - error.message = "data expressions not supported"; - return nullopt; - } else if (!(*expression).isFeatureConstant() || !(*expression).isZoomConstant()) { - return { std::move(*expression) }; - } else if ((*expression).getExpression().getKind() == Kind::Literal) { - optional<T> constant = fromExpressionValue<T>( - static_cast<const Literal&>((*expression).getExpression()).getValue()); - if (!constant) { - return nullopt; - } - return PropertyValue<T>(*constant); - } else { - assert(false); - error.message = "expected a literal expression"; - return nullopt; - } - } + optional<PropertyValue<T>> operator()(const Convertible& value, Error& error, bool allowDataExpressions, bool convertTokens) const; template <class S> PropertyValue<T> maybeConvertTokens(const S& t) const { diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp index 2cf2e36da4..19bc1ce6b6 100644 --- a/include/mbgl/style/conversion/source.hpp +++ b/include/mbgl/style/conversion/source.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/style/source.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> #include <memory> diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 1fb4acf70d..88661c358c 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -2,6 +2,7 @@ #include <mbgl/util/tileset.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp index 0563f39ac3..a72d757d3c 100644 --- a/include/mbgl/style/conversion/transition_options.hpp +++ b/include/mbgl/style/conversion/transition_options.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/transition_options.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion_impl.hpp b/include/mbgl/style/conversion_impl.hpp new file mode 100644 index 0000000000..27b2ee1917 --- /dev/null +++ b/include/mbgl/style/conversion_impl.hpp @@ -0,0 +1,302 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/feature.hpp> +#include <mbgl/util/geojson.hpp> + +#include <string> + +namespace mbgl { +namespace style { +namespace conversion { + +/* + The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by + the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain: + + * `std::unique_ptr<Source>` + * `std::unique_ptr<Layer>` + * `Filter` + * `PropertyValue<T>` + + A single template function serves as the public interface: + + template <class T> + optional<T> convert(const Convertible& input, Error& error); + + Where `T` is one of the above types. If the conversion fails, the result is empty, and the + error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, + a filled optional is returned. + + `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that + can serve as input to the conversion algorithm. For instance, on macOS, we need to support + conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc. + On Qt, we need to support conversion from RapidJSON types and QVariant. + + We don't want to use traditional forms of polymorphism to accomplish this: + + * Compile time polymorphism using a template parameter for the actual value type leads to + excessive code bloat and long compile times. + * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous + use of std::unique_ptr, unsuitable for this performance-sensitive code. + + Therefore, we're using a custom implementation of runtime polymorphism where we manually create and + dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible + underlying types inline on the stack, using `std::aligned_storage`. + + For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This + specialization must provide the following static methods: + + * `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null + + * `isArray(v)` -- returns a boolean indicating whether `v` represents a JSON array + * `arrayLength(v)` -- called only if `isArray(v)`; returns a size_t length + * `arrayMember(v)` -- called only if `isArray(v)`; returns `V` or `V&` + + * `isObject(v)` -- returns a boolean indicating whether `v` represents a JSON object + * `objectMember(v, name)` -- called only if `isObject(v)`; `name` is `const char *`; return value: + * is true when evaluated in a boolean context iff the named member exists + * is convertable to a `V` or `V&` when dereferenced + * `eachMember(v, [] (const std::string&, const V&) -> optional<Error> {...})` -- called + only if `isObject(v)`; calls the provided lambda once for each key and value of the object; + short-circuits if any call returns an `Error` + + * `toBool(v)` -- returns `optional<bool>`, absence indicating `v` is not a JSON boolean + * `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number + * `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number + * `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string + * `toValue(v)` -- returns `optional<Value>`, a variant type, for generic conversion, + absence indicating `v` is not a boolean, number, or string. Numbers should be converted to + unsigned integer, signed integer, or floating point, in descending preference. + + In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for + `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the + possible underlying types. (A static assert will fail if this is not the case.) + + `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state; + you must not do anything with it except let it go out of scope. +*/ + +template <typename T> +class ConversionTraits; + +class Convertible { +public: + template <typename T> + Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) { + static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type"); + new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value)); + } + + Convertible(Convertible&& v) + : vtable(v.vtable) + { + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + } + + ~Convertible() { + if (vtable) { + vtable->destroy(storage); + } + } + + Convertible& operator=(Convertible&& v) { + if (vtable) { + vtable->destroy(storage); + } + vtable = v.vtable; + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + v.vtable = nullptr; + return *this; + } + + Convertible() = delete; + Convertible(const Convertible&) = delete; + Convertible& operator=(const Convertible&) = delete; + + friend inline bool isUndefined(const Convertible& v) { + assert(v.vtable); + return v.vtable->isUndefined(v.storage); + } + + friend inline bool isArray(const Convertible& v) { + assert(v.vtable); + return v.vtable->isArray(v.storage); + } + + friend inline std::size_t arrayLength(const Convertible& v) { + assert(v.vtable); + return v.vtable->arrayLength(v.storage); + } + + friend inline Convertible arrayMember(const Convertible& v, std::size_t i) { + assert(v.vtable); + return v.vtable->arrayMember(v.storage, i); + } + + friend inline bool isObject(const Convertible& v) { + assert(v.vtable); + return v.vtable->isObject(v.storage); + } + + friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) { + assert(v.vtable); + return v.vtable->objectMember(v.storage, name); + } + + friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) { + assert(v.vtable); + return v.vtable->eachMember(v.storage, fn); + } + + friend inline optional<bool> toBool(const Convertible& v) { + assert(v.vtable); + return v.vtable->toBool(v.storage); + } + + friend inline optional<float> toNumber(const Convertible& v) { + assert(v.vtable); + return v.vtable->toNumber(v.storage); + } + + friend inline optional<double> toDouble(const Convertible& v) { + assert(v.vtable); + return v.vtable->toDouble(v.storage); + } + + friend inline optional<std::string> toString(const Convertible& v) { + assert(v.vtable); + return v.vtable->toString(v.storage); + } + + friend inline optional<Value> toValue(const Convertible& v) { + assert(v.vtable); + return v.vtable->toValue(v.storage); + } + + friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) { + assert(v.vtable); + return v.vtable->toGeoJSON(v.storage, error); + } + +private: +#if __ANDROID__ + // Android: JSValue* or mbgl::android::Value + using Storage = std::aligned_storage_t<32, 8>; +#elif __QT__ + // Qt: JSValue* or QVariant + using Storage = std::aligned_storage_t<32, 8>; +#else + // Node: JSValue* or v8::Local<v8::Value> + // iOS/macOS: JSValue* or id + using Storage = std::aligned_storage_t<8, 8>; +#endif + + struct VTable { + void (*move) (Storage&& src, Storage& dest); + void (*destroy) (Storage&); + + bool (*isUndefined) (const Storage&); + + bool (*isArray) (const Storage&); + std::size_t (*arrayLength) (const Storage&); + Convertible (*arrayMember) (const Storage&, std::size_t); + + bool (*isObject) (const Storage&); + optional<Convertible> (*objectMember) (const Storage&, const char *); + optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&); + + optional<bool> (*toBool) (const Storage&); + optional<float> (*toNumber) (const Storage&); + optional<double> (*toDouble) (const Storage&); + optional<std::string> (*toString) (const Storage&); + optional<Value> (*toValue) (const Storage&); + + // https://github.com/mapbox/mapbox-gl-native/issues/5623 + optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&); + }; + + // Extracted this function from the table below to work around a GCC bug with differing + // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 + template <typename T> + static auto vtableEachMember(const Storage& s, const std::function<optional<Error>(const std::string&, const Convertible&)>& fn) { + return ConversionTraits<T>::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) { + return fn(k, Convertible(std::move(v))); + }); + } + + template <typename T> + static VTable* vtableForType() { + using Traits = ConversionTraits<T>; + static VTable vtable = { + [] (Storage&& src, Storage& dest) { + auto srcValue = reinterpret_cast<T&&>(src); + new (static_cast<void*>(&dest)) T(std::move(srcValue)); + srcValue.~T(); + }, + [] (Storage& s) { + reinterpret_cast<T&>(s).~T(); + }, + [] (const Storage& s) { + return Traits::isUndefined(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::isArray(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::arrayLength(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, std::size_t i) { + return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i)); + }, + [] (const Storage& s) { + return Traits::isObject(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, const char * key) { + optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key); + if (member) { + return optional<Convertible>(Convertible(std::move(*member))); + } else { + return optional<Convertible>(); + } + }, + vtableEachMember<T>, + [] (const Storage& s) { + return Traits::toBool(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toNumber(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toDouble(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toString(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toValue(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, Error& err) { + return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err); + } + }; + return &vtable; + } + + VTable* vtable; + Storage storage; +}; + +template <class T, class...Args> +optional<T> convert(const Convertible& value, Error& error, Args&&...args) { + return Converter<T>()(value, error, std::forward<Args>(args)...); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp index 90da16b068..239cdf2ea6 100644 --- a/include/mbgl/style/expression/assertion.hpp +++ b/include/mbgl/style/expression/assertion.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> #include <memory> #include <vector> diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp index 02dc3bc4c2..7cd007d3c7 100644 --- a/include/mbgl/style/expression/case.hpp +++ b/include/mbgl/style/expression/case.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> #include <memory> #include <vector> diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp index cd60cee02e..c4216f234f 100644 --- a/include/mbgl/style/expression/coalesce.hpp +++ b/include/mbgl/style/expression/coalesce.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> #include <memory> #include <map> diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp index ef10dadb55..b54720a258 100644 --- a/include/mbgl/style/expression/compound_expression.hpp +++ b/include/mbgl/style/expression/compound_expression.hpp @@ -1,10 +1,10 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> #include <mbgl/style/expression/type.hpp> #include <mbgl/style/expression/value.hpp> +#include <mbgl/style/conversion.hpp> #include <mbgl/util/optional.hpp> #include <memory> diff --git a/include/mbgl/style/expression/parsing_context.hpp b/include/mbgl/style/expression/parsing_context.hpp index c19974a4f7..66014e33d4 100644 --- a/include/mbgl/style/expression/parsing_context.hpp +++ b/include/mbgl/style/expression/parsing_context.hpp @@ -7,6 +7,7 @@ #include <iterator> #include <map> +#include <unordered_map> #include <string> #include <vector> #include <memory> diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp index 12494f5387..3b7969ea79 100644 --- a/include/mbgl/style/layer.hpp +++ b/include/mbgl/style/layer.hpp @@ -1,10 +1,12 @@ #pragma once #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/unique_any.hpp> +#include <mbgl/util/peer.hpp> #include <mbgl/util/immutable.hpp> +#include <mbgl/util/optional.hpp> #include <mbgl/style/layer_type.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/style/conversion.hpp> #include <cassert> #include <memory> @@ -98,7 +100,6 @@ public: return std::forward<V>(visitor)(*as<HeatmapLayer>()); } - // Not reachable, but placate GCC. assert(false); throw new std::runtime_error("unknown layer type"); @@ -117,6 +118,11 @@ public: virtual void setMinZoom(float) = 0; virtual void setMaxZoom(float) = 0; + // Dynamic properties + virtual optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) = 0; + virtual optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) = 0; + optional<conversion::Error> setVisibility(const conversion::Convertible& value); + // Private implementation class Impl; Immutable<Impl> baseImpl; @@ -132,7 +138,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - util::unique_any peer; + util::peer peer; }; } // namespace style diff --git a/include/mbgl/style/layers/background_layer.hpp b/include/mbgl/style/layers/background_layer.hpp index eab2681fec..76230df12c 100644 --- a/include/mbgl/style/layers/background_layer.hpp +++ b/include/mbgl/style/layers/background_layer.hpp @@ -25,6 +25,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<Color> getDefaultBackgroundColor(); diff --git a/include/mbgl/style/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp index 89ef926221..cde691c893 100644 --- a/include/mbgl/style/layers/circle_layer.hpp +++ b/include/mbgl/style/layers/circle_layer.hpp @@ -33,6 +33,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultCircleRadius(); diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index fbe3a4a6c2..4b4c770489 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -75,6 +75,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Private implementation class Impl; diff --git a/include/mbgl/style/layers/fill_extrusion_layer.hpp b/include/mbgl/style/layers/fill_extrusion_layer.hpp index 742bac8c7e..e72fcade61 100644 --- a/include/mbgl/style/layers/fill_extrusion_layer.hpp +++ b/include/mbgl/style/layers/fill_extrusion_layer.hpp @@ -33,6 +33,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultFillExtrusionOpacity(); diff --git a/include/mbgl/style/layers/fill_layer.hpp b/include/mbgl/style/layers/fill_layer.hpp index d0b2a25bfe..430d7a011f 100644 --- a/include/mbgl/style/layers/fill_layer.hpp +++ b/include/mbgl/style/layers/fill_layer.hpp @@ -33,6 +33,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<bool> getDefaultFillAntialias(); diff --git a/include/mbgl/style/layers/heatmap_layer.hpp b/include/mbgl/style/layers/heatmap_layer.hpp index 53fd24aa6c..fd0051f44c 100644 --- a/include/mbgl/style/layers/heatmap_layer.hpp +++ b/include/mbgl/style/layers/heatmap_layer.hpp @@ -34,6 +34,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultHeatmapRadius(); diff --git a/include/mbgl/style/layers/hillshade_layer.hpp b/include/mbgl/style/layers/hillshade_layer.hpp index 214576b120..89d0ae686f 100644 --- a/include/mbgl/style/layers/hillshade_layer.hpp +++ b/include/mbgl/style/layers/hillshade_layer.hpp @@ -28,6 +28,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultHillshadeIlluminationDirection(); diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs index 9d52973af4..db7052387c 100644 --- a/include/mbgl/style/layers/layer.hpp.ejs +++ b/include/mbgl/style/layers/layer.hpp.ejs @@ -53,6 +53,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + <% if (layoutProperties.length) { -%> // Layout properties diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp index 26e3b81fc9..fe4cd7c0d1 100644 --- a/include/mbgl/style/layers/line_layer.hpp +++ b/include/mbgl/style/layers/line_layer.hpp @@ -35,6 +35,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Layout properties static PropertyValue<LineCapType> getDefaultLineCap(); diff --git a/include/mbgl/style/layers/raster_layer.hpp b/include/mbgl/style/layers/raster_layer.hpp index c133c23484..fcc35412a0 100644 --- a/include/mbgl/style/layers/raster_layer.hpp +++ b/include/mbgl/style/layers/raster_layer.hpp @@ -28,6 +28,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultRasterOpacity(); diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 8c0b45d796..fa0b0c4e4e 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -35,6 +35,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Layout properties static PropertyValue<SymbolPlacementType> getDefaultSymbolPlacement(); diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp index 2f2838ade8..dc3a8d73fb 100644 --- a/include/mbgl/style/source.hpp +++ b/include/mbgl/style/source.hpp @@ -2,7 +2,7 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/util/unique_any.hpp> +#include <mbgl/util/peer.hpp> #include <mbgl/util/immutable.hpp> #include <mbgl/style/types.hpp> @@ -77,7 +77,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - util::unique_any peer; + util::peer peer; }; } // namespace style diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index 372e7c7a78..a03b910279 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -18,6 +18,7 @@ struct GeoJSONOptions { uint16_t tileSize = util::tileSize; uint16_t buffer = 128; double tolerance = 0.375; + bool lineMetrics = false; // Supercluster options bool cluster = false; diff --git a/include/mbgl/util/expected.hpp b/include/mbgl/util/expected.hpp new file mode 100644 index 0000000000..a45f071065 --- /dev/null +++ b/include/mbgl/util/expected.hpp @@ -0,0 +1,16 @@ +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include <expected.hpp> +#pragma GCC diagnostic pop + +namespace mbgl { + +template <class T, class E> +using expected = nonstd::expected<T, E>; + +template <class E> +using unexpected = nonstd::unexpected_type<E>; + +} // namespace mbgl diff --git a/include/mbgl/util/font_stack.hpp b/include/mbgl/util/font_stack.hpp index d0b431e9ea..ace60a4ba6 100644 --- a/include/mbgl/util/font_stack.hpp +++ b/include/mbgl/util/font_stack.hpp @@ -1,7 +1,11 @@ #pragma once +#include <mbgl/util/immutable.hpp> +#include <mbgl/style/layer.hpp> + #include <string> #include <vector> +#include <set> namespace mbgl { @@ -14,4 +18,7 @@ struct FontStackHash { std::size_t operator()(const FontStack&) const; }; +// Statically evaluate layer properties to determine what font stacks are used. +std::set<FontStack> fontStacks(const std::vector<Immutable<style::Layer::Impl>>&); + } // namespace mbgl diff --git a/include/mbgl/util/peer.hpp b/include/mbgl/util/peer.hpp new file mode 100644 index 0000000000..a4abea0e88 --- /dev/null +++ b/include/mbgl/util/peer.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include <type_traits> +#include <utility> + +namespace mbgl { +namespace util { + +class peer { +public: + peer() = default; + peer(const peer&) = delete; + + peer(peer&& other) + : vtable(other.vtable) + { + if (vtable) { + vtable->move(other.storage, storage); + } + other.vtable = nullptr; + } + + template <class T> + peer(T&& value) { + using _Vt = std::decay_t<T>; + vtable = get_vtable<_Vt>(); + new (&storage) _Vt(std::forward<T>(value)); + } + + ~peer() { + reset(); + } + + peer& operator=(peer&& rhs) { + peer(std::move(rhs)).swap(*this); + return *this; + } + + void reset() { + if (vtable) { + vtable->destroy(storage); + vtable = nullptr; + } + } + + void swap(peer& rhs) { + if (this == &rhs) { + return; + } else { + peer tmp(std::move(rhs)); + rhs.vtable = vtable; + if (rhs.vtable) { + rhs.vtable->move(storage, rhs.storage); + } + vtable = tmp.vtable; + if (vtable) { + vtable->move(tmp.storage, storage); + } + } + } + + bool has_value() const { + return vtable != nullptr; + } + + template <class T> + T& get() { + return reinterpret_cast<T&>(storage); + } + + template <class T> + T&& take() { + reset(); + return std::move(get<T>()); + } + +private: + using storage_t = std::aligned_storage_t<2*sizeof(void*), alignof(void*)>; + + struct vtable { + virtual ~vtable() = default; + virtual void move(storage_t&, storage_t&) = 0; + virtual void destroy(storage_t&) = 0; + }; + + template <class T> + struct vtable_impl : public vtable { + static_assert(sizeof(T) <= sizeof(storage_t), "peer object is too big"); + + void move(storage_t& src, storage_t& dst) override { + new (&dst) T(std::move(reinterpret_cast<T&>(src))); + destroy(src); + } + + void destroy(storage_t& s) override { + reinterpret_cast<T&>(s).~T(); + } + }; + + template <class T> + static vtable* get_vtable() { + static vtable_impl<T> vtable; + return &vtable; + } + + vtable* vtable = nullptr; + storage_t storage; +}; + +} // namespace util +} // namespace mbgl diff --git a/include/mbgl/util/unique_any.hpp b/include/mbgl/util/unique_any.hpp deleted file mode 100644 index c7dc8b38ea..0000000000 --- a/include/mbgl/util/unique_any.hpp +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once - -#include <typeinfo> -#include <type_traits> -#include <stdexcept> -namespace mbgl { -namespace util { - -class bad_any_cast : public std::bad_cast { -public: - const char* what() const noexcept override { - return "bad any_cast<>()"; - } -}; -/** - * A variant of `std::any` for non-copyable types. - * - * Use `unique_any` for non-copyable types (e.g. `std::unique_ptr<T>`) - * or to ensure that no copies are made of copyable types that are - * moved in. - * - * `uniqe_any` differs from `std::any` in that it does not support copy construction - * or copy assignment. It also does not require the contained type to be copy - * constructible. - * - * The `any_cast<T>()` methods work similar to `std::any_cast<T>()` except that - * non-copyable types may only be cast to references. - * - * Example usage: - * unique_any u1(3); - * auto u2 = unique_any(std::move(u1)); // u1 is moved from - * int i = any_cast<int>(u2); - * - * unique_any u2; - * u2 = std::unique_ptr<int>(new int); - * std::unique_ptr<int> iPtr = any_cast<std::unique_ptr<int>>(std::move(u2)); - * - * Inspired by linb::any (https://github.com/thelink2012/any) and the - * libc++ implementation (https://github.com/llvm-mirror/libcxx). - */ -class unique_any final -{ -public: - unique_any() = default; - - //Copy constructor (deleted) - unique_any(const unique_any& rhs) = delete; - - unique_any(unique_any&& rhs) : vtable(rhs.vtable) { - if (vtable) { - vtable->move(std::move(rhs.storage), storage); - } - rhs.vtable = nullptr; - } - - // Constructs with a direct-initilizated object of type ValueType - template <typename ValueType, - typename _Vt = std::decay_t<ValueType>, - typename = std::enable_if_t<!std::is_same<_Vt, unique_any>::value> > - unique_any(ValueType&& value) { - create(std::forward<ValueType>(value)); - } - - ~unique_any() { - reset(); - } - - unique_any& operator=(unique_any&& rhs) { - unique_any(std::move(rhs)).swap(*this); - return *this; - } - - template <class ValueType, - typename = std::enable_if_t<!std::is_same<std::decay_t<ValueType>, unique_any>::value> > - unique_any& operator=(ValueType&& rhs) { - unique_any(std::forward<ValueType>(rhs)).swap(*this); - return *this; - } - - void reset() { - if (vtable) { - vtable->destroy(storage); - vtable = nullptr; - } - } - - void swap(unique_any& rhs) { - if (this == &rhs) { - return; - } else { - unique_any tmp(std::move(rhs)); - rhs.vtable = vtable; - if (rhs.vtable) { - rhs.vtable->move(std::move(storage), rhs.storage); - } - vtable = tmp.vtable; - if (vtable) { - vtable->move(std::move(tmp.storage), storage); - } - } - } - - const std::type_info& type() const { - return !has_value()? typeid(void) : vtable->type(); - } - - bool has_value() const { - return vtable != nullptr; - } - -private: - - union Storage { - using StackStorage = std::aligned_storage_t<3*sizeof(void*), std::alignment_of<void*>::value>; - Storage() = default; - - void * dynamic { nullptr }; - StackStorage stack; - }; - - template<typename T> - struct AllocateOnStack : std::integral_constant<bool, - sizeof(T) <= sizeof(Storage::stack) - && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorage>::value - && std::is_nothrow_move_constructible<T>::value> { - }; - - struct VTable { - virtual ~VTable() = default; - virtual void move(Storage&& src, Storage& dest) = 0; - virtual void destroy(Storage&) = 0; - virtual const std::type_info& type() = 0; - }; - - template <typename ValueType> - struct VTableHeap : public VTable { - void move(Storage&& src, Storage& dest) override { - dest.dynamic = src.dynamic; - src.dynamic = nullptr; - } - - void destroy(Storage& s) override { - delete reinterpret_cast<ValueType*>(s.dynamic); - } - - const std::type_info& type() override { - return typeid(ValueType); - } - }; - - template <typename ValueType> - struct VTableStack : public VTable { - void move(Storage&& src, Storage& dest) override { - new (&dest.stack) ValueType(std::move(reinterpret_cast<ValueType&>(src.stack))); - destroy(src); - } - - void destroy(Storage& s) override { - reinterpret_cast<ValueType&>(s.stack).~ValueType(); - } - - const std::type_info& type() override { - return typeid(ValueType); - } - }; - - template <typename ValueType> - static VTable* vtableForType() { - using VTableType = std::conditional_t<AllocateOnStack<ValueType>::value, VTableStack<ValueType>, VTableHeap<ValueType> >; - static VTableType vtable; - return &vtable; - } - - template <typename ValueType, typename _Vt> - std::enable_if_t<AllocateOnStack<_Vt>::value> - createStorage(ValueType&& value) { - new (&storage.stack) _Vt(std::forward<ValueType>(value)); - } - - template <typename ValueType, typename _Vt> - std::enable_if_t<!AllocateOnStack<_Vt>::value> - createStorage(ValueType&& value) { - storage.dynamic = new _Vt(std::forward<ValueType>(value)); - } - - template <typename ValueType> - void create(ValueType&& value) { - using _Vt = std::decay_t<ValueType>; - vtable = vtableForType<_Vt>(); - createStorage<ValueType, _Vt>(std::forward<ValueType>(value)); - } - - VTable* vtable { nullptr }; - Storage storage; - -protected: - template<class ValueType> - friend const ValueType* any_cast(const unique_any* operand) ; - - template<class ValueType> - friend ValueType* any_cast(unique_any* operand) ; - - template<typename ValueType, typename _Vt = std::decay_t<ValueType> > - ValueType* cast() - { - return reinterpret_cast<ValueType *>( - AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic); - } -}; - -template<typename ValueType> -inline const ValueType* any_cast(const unique_any* any) -{ - return any_cast<ValueType>(const_cast<unique_any *>(any)); -} - -template<typename ValueType> -inline ValueType* any_cast(unique_any* any) -{ - if(any == nullptr || any->type() != typeid(ValueType)) - return nullptr; - else - return any->cast<ValueType>(); -} - -template<typename ValueType, typename _Vt = std::decay_t<ValueType> > -inline ValueType any_cast(const unique_any& any) -{ - static_assert(std::is_constructible<ValueType, const _Vt&>::value, - "any_cast type can't construct copy of contained object"); - auto temp = any_cast<_Vt>(&any); - if (temp == nullptr) { - throw bad_any_cast(); - } - return static_cast<ValueType>(*temp); -} - -template<typename ValueType, typename _Vt = std::decay_t<ValueType> > -inline ValueType any_cast(unique_any& any) -{ - static_assert(std::is_constructible<ValueType, const _Vt&>::value, - "any_cast type can't construct copy of contained object"); - auto temp = any_cast<_Vt>(&any); - if (temp == nullptr) { - throw bad_any_cast(); - } - return static_cast<ValueType>(*temp); -} - -template<typename ValueType, typename _Vt = std::remove_cv_t<ValueType> > -inline ValueType any_cast(unique_any&& any) -{ - auto temp = any_cast<_Vt>(&any); - if (temp == nullptr) { - throw bad_any_cast(); - } - auto retValue = static_cast<ValueType>(std::move(*temp)); - any.reset(); - return std::move(retValue); -} - -} // namespace util -} // namespace mbgl - -namespace std { - -inline void swap(mbgl::util::unique_any& lhs, mbgl::util::unique_any& rhs) { - lhs.swap(rhs); -} - -} // namespace std |